// cl_LF internals, transcendental functions

#ifndef _CL_LF_TRAN_H
#define _CL_LF_TRAN_H

#include "cl_integer.h"
#include "cl_lfloat.h"


// Subroutine for evaluating
// sum(0 <= n < N, a(n)/b(n) * (p(0)...p(n))/(q(0)...q(n)))
// where all the entries are small integers (ideally polynomials in n).
// This is fast because it groups factors together before multiplying.
// Arguments:
//   Vectors p[0..N-1], q[0..N-1], a[0..N-1], b[0..N-1], N.
//   Some of the vectors (a,b,p,q) can be a NULL pointer, all of its entries
//   are then understood to be 1.
//   If given, a vector qs[0..N-1] which the evaluation routine may use to
//   split off q[n] into q[n]*2^qs[n]. qs may be NULL, in that case no shift
//   optimizations will be used. (They are worth it only if a significant
//   amount of multiplication work can be saved by shifts.)
// Result will be a cl_LF with len digits.
struct cl_rational_series {
	// To be set explicitly.
	const cl_I* pv;
	      cl_I* qv;
	const cl_I* av;
	const cl_I* bv;
	     uintL* qsv;
};
extern cl_LF eval_rational_series (uintL N, const cl_rational_series& args, uintC len);

// In this alternate implementation the series is not represented as a couple
// of arrays, but as a method returning each tuple (p(n),q(n),a(n),b(n))
// in turn. This is preferrable if the a(n) are big, in order to avoid too
// much memory usage at the same time.
// Some of the factors (a,b) may be omitted. They are then understood to be 1.
// The next function is called N times and is expected to return
// (p(n),q(n),a(n),b(n)) for n=0..N-1 in that order.
//
struct cl_rational_series_pqab {
	cl_I p;
	cl_I q;
	cl_I a;
	cl_I b;
};
struct cl_rational_series_pqab_stream {
	cl_rational_series_pqab (*nextfn)(cl_rational_series_pqab_stream&);
	cl_rational_series_pqab next () { return nextfn(*this); }
	// Constructor.
	cl_rational_series_pqab_stream (cl_rational_series_pqab (*n)(cl_rational_series_pqab_stream&)) : nextfn (n) {}
};
extern cl_LF eval_rational_series (uintL N, cl_rational_series_pqab_stream& args, uintC len);
//
struct cl_rational_series_pqb {
	cl_I p;
	cl_I q;
	cl_I b;
};
struct cl_rational_series_pqb_stream {
	cl_rational_series_pqb (*nextfn)(cl_rational_series_pqb_stream&);
	cl_rational_series_pqb next () { return nextfn(*this); }
	// Constructor.
	cl_rational_series_pqb_stream (cl_rational_series_pqb (*n)(cl_rational_series_pqb_stream&)) : nextfn (n) {}
};
extern cl_LF eval_rational_series (uintL N, cl_rational_series_pqb_stream& args, uintC len);
//
struct cl_rational_series_pqa {
	cl_I p;
	cl_I q;
	cl_I a;
};
struct cl_rational_series_pqa_stream {
	cl_rational_series_pqa (*nextfn)(cl_rational_series_pqa_stream&);
	cl_rational_series_pqa next () { return nextfn(*this); }
	// Constructor.
	cl_rational_series_pqa_stream (cl_rational_series_pqa (*n)(cl_rational_series_pqa_stream&)) : nextfn (n) {}
};
extern cl_LF eval_rational_series (uintL N, cl_rational_series_pqa_stream& args, uintC len);
//
struct cl_rational_series_pq {
	cl_I p;
	cl_I q;
};
struct cl_rational_series_pq_stream {
	cl_rational_series_pq (*nextfn)(cl_rational_series_pq_stream&);
	cl_rational_series_pq next () { return nextfn(*this); }
	// Constructor.
	cl_rational_series_pq_stream (cl_rational_series_pq (*n)(cl_rational_series_pq_stream&)) : nextfn (n) {}
};
extern cl_LF eval_rational_series (uintL N, cl_rational_series_pq_stream& args, uintC len);


// [Generalization.]
// Subroutine:
// Evaluates S = sum(N1 <= n < N2, (p(N1)...p(n))/(q(N1)...q(n)))
// and U = sum(N1 <= n < N2,
//             (c(N1)/d(N1)+...+c(n)/d(n))*(p(N1)...p(n))/(q(N1)...q(n)))
// and returns
//     P = p(N1)...p(N2-1),
//     Q = q(N1)...q(N2-1),
//     T = Q*S,
//     C/D = c(N1)/d(N1)+...+c(N2-1)/d(N2-1),
//     V = D*Q*U,
// all integers. On entry N1 < N2.
struct pqcd_series_term {
	cl_I p;
	cl_I q;
	cl_I c;
	cl_I d;
};
struct pqcd_series_result {
	cl_I P;
	cl_I Q;
	cl_I T;
	cl_I C;
	cl_I D;
	cl_I V;
};
extern void eval_pqcd_series_aux (uintL N, pqcd_series_term* args, pqcd_series_result& Z, cl_boolean rightmost = cl_true);
// Ditto, but returns U/S.
extern cl_LF eval_pqcd_series (uintL N, pqcd_series_term* args, uintC len);


// [Special case c(n)=1.]
// Subroutine:
// Evaluates S = sum(N1 <= n < N2, (p(N1)...p(n))/(q(N1)...q(n)))
// and U = sum(N1 <= n < N2, (1/d(N1)+...+1/d(n))*(p(N1)...p(n))/(q(N1)...q(n)))
// and returns
//     P = p(N1)...p(N2-1),
//     Q = q(N1)...q(N2-1),
//     T = Q*S,
//     C/D = 1/d(N1)+...+1/d(N2-1),
//     V = D*Q*U,
// all integers. On entry N1 < N2.
struct pqd_series_term {
	cl_I p;
	cl_I q;
	cl_I d;
};
struct pqd_series_result {
	cl_I P;
	cl_I Q;
	cl_I T;
	cl_I C;
	cl_I D;
	cl_I V;
};
extern void eval_pqd_series_aux (uintL N, pqd_series_term* args, pqd_series_result& Z, cl_boolean rightmost = cl_true);
// Ditto, but returns U/S.
extern cl_LF eval_pqd_series (uintL N, pqd_series_term* args, uintC len);


#endif /* _CL_LF_TRAN_H */
