%mathpiper,def="BracketRational"

//////////////////////////////////////////////////
/// BracketRational: find two rational approximations
//////////////////////////////////////////////////

/// Return a list of two rational numbers r1, r2 such that r1<r<r2 and |r2-r1| < eps*|r|
BracketRational(r,eps):=
[
	Local(n,cflist, r1, r2);
	cflist := ContFracList(r);
	n:=2;
	r1 := ContFracEval(Take(cflist,n));
	r2 := -r1;
	// find two successive approximations and check that they differ by less than |eps*r|
	While (n<Length(cflist) And ( Abs(N(Eval(r2-r1))) > Abs(N(Eval(eps*r)) ) ) )
	[
		r2 := r1;
		n++;
		r1 := ContFracEval(Take(cflist,n));
	];
	// now r1 and r2 are some rational numbers.
	// decide whether the search was successful.
	If(
		n=Length(cflist),
		{},	// return empty list if not enough precision
		If(N(Eval(r-r1))>0,
			{r1, r2},	// successive approximations are always bracketing, we only need to decide their order
			{r2, r1}
		)
	);
];

%/mathpiper



%mathpiper_docs,name="BracketRational",categories="User Functions;Numbers (Operations)"
*CMD BracketRational --- find optimal rational approximations
*STD
*CALL
	BracketRational(x, eps)

*PARMS

{x} -- a number to be approximated (must be already evaluated to floating-point)

{eps} -- desired precision

*DESC

The function {BracketRational(x,eps)} can be used to find approximations with a given relative precision from above and from below.
This function returns a list of two rational numbers {{r1,r2}} such that $r1<x<r2$ and $Abs(r2-r1)<Abs(x*eps)$.
The argument {x} must be already evaluated to enough precision so that this approximation can be meaningfully found.
If the approximation with the desired precision cannot be found, the function returns an empty list.

*E.G.

Find an approximation to $Ln(10)$ good to 8 digits:
In> BracketRational(N(Ln(10)), 10^(-8))
Result: {12381/5377,41062/17833};


*SEE GuessRational, NearRational, ContFrac, ContFracList, Rationalize
%/mathpiper_docs
