%mathpiper,def="BinSplitNum;BinSplitData;BinSplitFinal"

/*
def file definitions
BinSplitNum
BinSplitData
BinSplitFinal
*/

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

//////////////////////////////////////////////////
/// Numerical method: binary splitting technique for simple series
//////////////////////////////////////////////////

/// Binary splitting for series of the form
/// S(m,n) = Sum(k,m,n, a(k)/b(k)*(p(0)*...*p(k))/(q(0)*...*q(k)))


/// High-level interface routine
BinSplitNum(m,n,a,b,p,q) := BinSplitFinal(BinSplitData(m,n,a,b,p,q));

/// Low-level routine: compute the floating-point answer from P, Q, B, T data
BinSplitFinal({_P,_Q,_B,_T}) <-- DivideN(T, MultiplyN(B, Q));

/// Low-level routine: combine two binary-split intermediate results
BinSplitCombine({_P1, _Q1, _B1, _T1}, {_P2, _Q2, _B2, _T2}) <-- {P1*P2, Q1*Q2, B1*B2, B1*P1*T2+B2*Q2*T1};

/// Low-level routine: compute the list of four integers P, Q, B, T. (T=BQS)
/// Input: m, n and four functions a,b,p,q of one integer argument.

// base of recursion
10 # BinSplitData(_m, _n, _a, _b, _p, _q)_(m>n) <-- {1,1,1,0};

10 # BinSplitData(_m, _n, _a, _b, _p, _q)_(m=n) <-- {p@m, q@m, b@m, (a@m)*(p@m)};

10 # BinSplitData(_m, _n, _a, _b, _p, _q)_(m+1=n) <-- {(p@m)*(p@n), (q@m)*(q@n), (b@m)*(b@n), (p@m)*((a@m)*(b@n)*(q@n)+(a@n)*(b@m)*(p@n))};

// could implement some more cases of recursion base, to improve speed

// main recursion step
20 # BinSplitData(_m, _n, _a, _b, _p, _q) <--
[
	BinSplitCombine(BinSplitData(m,(m+n)>>1, a,b,p,q), BinSplitData(1+((m+n)>>1),n, a,b,p,q));
];


%/mathpiper



%mathpiper_docs,name="BinSplitNum;BinSplitData;BinSplitFinal",categories="User Functions;Series"
*CMD BinSplitNum --- computations of series by the binary splitting method
*CMD BinSplitData --- computations of series by the binary splitting method
*CMD BinSplitFinal --- computations of series by the binary splitting method
*STD
*CALL
	BinSplitNum(n1, n2, a, b, c, d)
	BinSplitData(n1,n2, a, b, c, d)
	BinSplitFinal({P,Q,B,T})

*PARMS

{n1}, {n2} -- integers, initial and final indices for summation

{a}, {b}, {c}, {d} -- functions of one argument, coefficients of the series

{P}, {Q}, {B}, {T} -- numbers, intermediate data as returned by {BinSplitData}

*DESC

The binary splitting method is an efficient way to evaluate many series when fast multiplication is available and when the series contains only rational numbers.
The function {BinSplitNum} evaluates a series of the form
$$ S(n[1],n[2])=Sum(k,n[1],n[2], a(k)/b(k)*(p(0)/q(0)) * ... * p(k)/q(k)) $$.
Most series for elementary and special functions at rational points are of this form when the functions $a(k)$, $b(k)$, $p(k)$, $q(k)$ are chosen appropriately.

The last four arguments of {BinSplitNum} are functions of one argument that give the coefficients $a(k)$, $b(k)$, $p(k)$, $q(k)$.
In most cases these will be short integers that are simple to determine.
The binary splitting method will work also for non-integer coefficients, but the calculation will take much longer in that case.

Note: the binary splitting method outperforms the straightforward summation only if the multiplication of integers is faster than quadratic in the number of digits.
See <*the algorithm documentation|mathpiperdoc://Algo/3/14/*> for more information.

The two other functions are low-level functions that allow a finer control over the calculation.
The use of the low-level routines allows checkpointing or parallelization of a binary splitting calculation.

The binary splitting method recursively reduces the calculation of $S(n[1],n[2])$ to the same calculation for the two halves of the interval [$n[1]$, $n[2]$].
The intermediate results of a binary splitting calculation are returned by {BinSplitData} and consist of four integers $P$, $Q$, $B$, $T$.
These four integers are converted into the final answer $S$ by the routine {BinSplitFinal} using the relation
$$ S = T / (B*Q) $$.

*E.G.

Compute the series for $e=Exp(1)$ using binary splitting.
(We start from $n=1$ to simplify the coefficient functions.)
In> BuiltinPrecisionSet(21)
Result: True;
In>  BinSplitNum(1,21, {{k},1},
	  {{k},1},{{k},1},{{k},k})
Result: 1.718281828459045235359;
In> N(Exp(1)-1)
Result: 1.71828182845904523536;
In>  BinSplitData(1,21, {{k},1},
	  {{k},1},{{k},1},{{k},k})
Result: {1,51090942171709440000,1,
	  87788637532500240022};
In> BinSplitFinal(%)
Result: 1.718281828459045235359;

*SEE SumTaylorNum
%/mathpiper_docs