/*
 * consts.cpp: spigot implementations of mathematical constants.
 */

#include "spigot.h"
#include "funcs.h"
#include "cr.h"

class E : public Source {
    int k { 1 };

    virtual Spigot clone() override;
    virtual bool gen_interval(bigint *low, bigint_or_inf *high) override;
    virtual bool gen_matrix(Matrix &matrix) override;

  public:
    E();
};

MAKE_CLASS_GREETER(E);

E::E()
{
    dgreet();
}

Spigot E::clone()
{
    return spigot_clone(this);
}

/*
 * The spigot definition for e is derived from the obvious series for
 * e obtained by expanding the Taylor series for exp(1):
 *
 *  e = 1 + 1/1! + 1/2! + 1/3! + 1/4! + ...
 *    = 1 + 1/1 * (1 + 1/2 * (1 + 1/3 * (1 + 1/4 * *...)))
 *
 * which gives us the infinite function composition
 *
 *   (x |-> 1+x) o (x |-> 1+x/2) o (x |-> 1+x/3) o ...
 *
 * and hence the sequence of matrices
 *
 *   ( 1 1 ) ( 1 2 ) ( 1 3 ) ... ( 1 k ) ...
 *   ( 0 1 ) ( 0 2 ) ( 0 3 )     ( 0 k )
 *
 * We must also adjust the limits 3 and 4. We picked the interval
 * [3,4] for the pi expansion because we know that each of the
 * functions in the infinite composition for pi maps [3,4] to a
 * subinterval of itself, meaning that the images of the ends of that
 * interval always bracket pi. However, this is not true for the above
 * series of functions for e. In fact, there is _no_ interval which
 * all of the above functions map to a subinterval of itself, because
 * the first function (x |-> 1+x) translates every interval upwards!
 *
 * Fortunately, all the functions _other_ than the first one have the
 * property that they map [0,2] to a subinterval of itself. So once
 * the image of [0,2] under a `partial sum' of at least two terms of
 * this function series contains e, its image under subsequent partial
 * sums will be a subinterval of that and hence will always contain e
 * as well. So we need only check that no spurious `digits' are output
 * before that happens, and then we're safe. And this is OK, because
 * the identity matrix maps [0,2] to [0,2] which doesn't have a
 * constant integer part, the first function maps it to [1,3] which
 * doesn't either, and the first two functions map it to [2,3] which
 * contains e and is late enough in the series to be safe.
 */

bool E::gen_interval(bigint *low, bigint_or_inf *high)
{
    *low = 0;
    *high = 2;
    return true;
}

bool E::gen_matrix(Matrix &matrix)
{
    matrix = { 1, k, 0, k };
    k++;
    return false;
}

/*
 * The series component of Chudnovsky's formula for pi:
 * https://en.wikipedia.org/wiki/Chudnovsky_algorithm
 *
 * The series has the general form
 *
 *   c_0 - m_0 c_1 + m_0 m_1 c_2 - m_0 m_1 m_2 c_3 + ...
 *
 * in which c_n is the arithmetic progression (13591409 + 545140134n),
 * and m_n is a product of assorted linear factors and a very large
 * constant in the denominator giving fast convergents.
 *
 * It's therefore tempting to factorise the series in the obvious
 * spigot fashion, and convert it into a sequence of transformations
 *
 *           c_0 - m_0 (c_1 - m_1 (c_2 - m_2 (c_3 - ...)))
 *
 * ->        (x |-> c_0 - m_0 x) o
 *           (x |-> c_1 - m_1 x) o
 *           (x |-> c_2 - m_2 x) o ...
 *
 * But the problem with this is that you can't choose a starting
 * interval such that all those transformations are refining. Proof:
 * the c_i are unbounded, so sooner or later you'll reach one large
 * enough that the image of the low end of the starting interval (i.e.
 * the high end of the transformed interval) exceeds the high end of
 * the starting interval.
 *
 * To fix this, we replace the additive component c_n with c_n/(n+1),
 * so that now it has a finite limit, namely the step value 545140134.
 * So our actual transform sequence looks like this:
 *
 *           (x |-> c_0   - 2   m_0 x) o
 *           (x |-> c_1/2 - 3/2 m_1 x) o
 *           (x |-> c_2/3 - 4/3 m_2 x) o
 *           (x |-> c_2/4 - 5/4 m_3 x) o ...
 *
 * and _now_ we can make a starting interval that works.
 *
 * This series doesn't compute pi itself: it computes a value from
 * which you can derive pi by taking the reciprocal and multiplying by
 * an integer times sqrt(10005). That's done in spigot_pi_inner() below.
 */
class ChudnovskyPiSeries : public Source {
    bigint c { 13591409_bi }, k { 0 }, f { 1 };

    virtual Spigot clone() override;
    virtual bool gen_interval(bigint *low, bigint_or_inf *high) override;
    virtual bool gen_matrix(Matrix &matrix) override;

  public:
    ChudnovskyPiSeries();
};

MAKE_CLASS_GREETER(ChudnovskyPiSeries);

ChudnovskyPiSeries::ChudnovskyPiSeries()
{
    dgreet();
}

Spigot ChudnovskyPiSeries::clone()
{
    return spigot_clone(this);
}

bool ChudnovskyPiSeries::gen_interval(bigint *low, bigint_or_inf *high)
{
    /*
     * Possibly the largest _finite_ starting interval in the whole of
     * spigot? This interval is inverted by every matrix, so the
     * output high end will be the image of 0, which is equal to the
     * additive term of the transformation x |-> c_n/(n+1) + m_n x.
     * Those terms form a sequence tending to 545140134 from below, so
     * the high end of our starting interval has to be at least that.
     */
    *low = -0_bi;
    *high = 545140134_bi;
    return true;
}

bool ChudnovskyPiSeries::gen_matrix(Matrix &matrix)
{
    bigint n = (k + 1) * (k + 3) * (k + 5);
    k += 6;
    bigint d = k*k*k * 151931373056000_bi;

    bigint fold = f;
    ++f;

    matrix = { -n * f, c * d, 0, d * fold };
    c += 545140134_bi;

    return false;
}

/*
 * Apery's constant: zeta(3), or the sum of the reciprocals of the
 * cubes of the natural numbers.
 */
class Apery : public Source {
    int crState { -1 };
    bigint k;

    virtual Spigot clone() override;
    virtual bool gen_interval(bigint *low, bigint_or_inf *high) override;
    virtual bool gen_matrix(Matrix &matrix) override;

  public:
    Apery();
};

MAKE_CLASS_GREETER(Apery);

Apery::Apery()
{
    dgreet();
}

Spigot Apery::clone()
{
    return spigot_clone(this);
}

bool Apery::gen_interval(bigint *low, bigint_or_inf *high)
{
    *low = -1;
    *high = +1;
    return true;
}

bool Apery::gen_matrix(Matrix &matrix)
{
    crBegin;

    /*
     * We spigotise the really simple series of Hjortnaes (source:
     * Wikipedia)
     *
     *           5          k-1    k!^2
     * zeta(3) = -  sum (-1)    ---------
     *           2 k>=1         k^3 (2k)!
     *
     * into a sequence of spigot matrices in which each one adds
     * 1/k^3, and multiplies in a factor n/d converting k!^2/(2k)!
     * into the same thing for k+1. (The factor also includes the sign
     * flip.)
     */

    /*
     * x |-> 5/2 x
     */
    matrix = { 5, 0, 0, 2 };
    crReturn(true);

    for (k = 1 ;; ++k) {
        {
            bigint k3 = k*k*k;
            bigint n = k*k, d = (2*k-1) * (2*k);

            /*
             * x |-> n/d (1/k3 - x) = (-n k3 x + n) / (0x + d k3)
             */
            matrix = { -n*k3, n, 0, d*k3 };
        }

        crReturn(false);
    }

    crEnd;
}

/*
 * phi, in the 'golden ratio' sense.
 *
 * We could set up a low-level Source definition for phi in terms of
 * spigot matrices, but it's more easily defined in terms of its
 * continued fraction, which consists of nothing but 1s.
 */
class Phi : public CfracSource {
    virtual Spigot clone() override;
    virtual bool gen_term(bigint *term) override;

  public:
    Phi();
};

MAKE_CLASS_GREETER(Phi);

Phi::Phi()
{
    dgreet();
}

Spigot Phi::clone()
{
    return spigot_clone(this);
}

bool Phi::gen_term(bigint *term)
{
    *term = 1;
    return true;
}

/*
 * Catalan's constant G [1] [2].
 *
 * Theorem 1 of [3] states that the constant is given by the following
 * alternating series:
 *
 *
 *       1 inf               (3n+2) 8^n
 *   G = - sum (-1)^n ------------------------
 *       2 n=0        (2n+1)^3 (2n choose n)^3
 *
 * In the spigot world, non-alternating series are easier to manage,
 * so we instead combine adjacent pairs of terms to give a condensed
 * series with all terms positive.
 *
 * Figuring out what that actually comes to is a painful piece of
 * algebra, so I resorted to Maxima to do the heavy lifting for me.
 * The following commands (with a bit of a bodge using subst to
 * account for the fact that Maxima doesn't automatically spot that
 * the ratio of two closely related binomial coefficients can be
 * reduced to a simpler rational-function form):
 *
 *   term : lambda([n], (3*n+2) * 8^n / ((2*n+1) * binomial(2*n,n))^3 );
 *   factor(subst([binomial(2*(n+1),n+1)=(4*n+2)/(n+1)*binomial(2*n,n)],
 *                term(n) - term(n+1)));
 *
 * give the following output expression:
 *
 *                      2        3       4   n
 *   (49 + 171 n + 210 n  + 110 n  + 21 n ) 8
 *   -----------------------------------------
 *             3          3         3
 *    (1 + 2 n)  (3 + 2 n)  binomial (2 n, n)
 *
 * So we're going to sum _that_, for n=0,2,4,6,8,...
 *
 * In the usual spigot style, we aim to 'factorise' this series into
 * something of the form
 *
 *   G = a_0 + m_0 a_1 + (m_0 m_1) a_2 + (m_0 m_1 m_2) a_3 + ...
 *     = a_0 + m_0 (a_1 + m_1 (a_2 + m_2 (...)))
 *
 * so that we can represent it as the composition of a series of
 * Mobius transformations of the form x |-> a_n + m_n x.
 *
 * In this case, the easiest way is to let a_n be the
 * rational-function part of the above expression (the quartic on top,
 * divided by the two cubed linear terms on the bottom), and choose
 * the m_n so that their product comes to 8^n / (2n choose n)^3,
 * because it's easy to work out the ratio between two successive
 * values of that expression, namely 8^2 (n+1)^3 / (4n+2)^3.
 *
 * [1] https://oeis.org/A006752
 * [2] https://en.wikipedia.org/wiki/Catalan%27s_constant
 * [3] 'A rapidly converging Ramanujan-type series for Catalan's
 *     constant'. F. M. S. Lima. https://arxiv.org/abs/1207.3139
 */
class Catalan : public Source {
    bigint n { 0 };

    virtual Spigot clone() override;
    virtual bool gen_interval(bigint *low, bigint_or_inf *high) override;
    virtual bool gen_matrix(Matrix &matrix) override;

  public:
    Catalan();
};

MAKE_CLASS_GREETER(Catalan);

Catalan::Catalan()
{
    dgreet();
}

Spigot Catalan::clone()
{
    return spigot_clone(this);
}

bool Catalan::gen_interval(bigint *low, bigint_or_inf *high)
{
    // G is in the range [0,1] (in fact it's about 0.916), so this
    // interval will include the true final value. As we transform it,
    // the base of the transformed interval will equal the sum of all
    // the terms so far, and the top will be an adequate upper bound
    // on the sum of all the subsequent terms.
    *low = 0;
    *high = 1;
    return true;
}

bool Catalan::gen_matrix(Matrix &matrix)
{
    // Numerator and denominator of a_n, the rational-function part of
    // the summand.
    bigint a_num = 49 + n * (171 + n * (210 + n * (110 + n * 21)));
    bigint a_den_cbrt = (2*n + 1) * (2*n + 3);
    bigint a_den = a_den_cbrt * a_den_cbrt * a_den_cbrt;

    // Numerator and denominator of m_n, the ratio between successive
    // values of the multiplicative part.
    bigint m_num_cbrt = (n+1) * (n+2) * 4;
    bigint m_den_cbrt = (4*n + 2) * (4*n + 6);
    bigint m_num = m_num_cbrt * m_num_cbrt * m_num_cbrt;
    bigint m_den = m_den_cbrt * m_den_cbrt * m_den_cbrt;

    // In general, a/b + c/d x = (bc x + ad) / (0 x + bd).
    matrix = { m_num * a_den, a_num * m_den, 0, a_den * m_den };

    // Account for the fact that G is actually _half_ the sum of this
    // series, by including a factor of 1/2 on the first matrix we
    // return.
    if (!n)
        matrix[3] *= 2;

    n += 2;
    return false;
}

Spigot spigot_e()
{
    return make_unique<E>();
}

static const ConstantFnWrapper expr_e{"e", spigot_e};

static Spigot spigot_pi_inner(int multiple)
{
    // This takes a multiplier parameter so as to generate both pi and tau
    return spigot_combine(spigot_sqrt(spigot_integer(10005)),
                          make_unique<ChudnovskyPiSeries>(),
                          Tensor {0, multiple*426880, 0, 0, 0, 0, 1, 0});
}

Spigot spigot_pi() { return spigot_pi_inner(1); }
static Spigot spigot_tau() { return spigot_pi_inner(2); }

static const ConstantFnWrapper expr_pi{"pi", spigot_pi};
static const ConstantFnWrapper expr_tau{"tau", spigot_tau};

static Spigot spigot_chudnovsky_pi_series()
{
    return make_unique<ChudnovskyPiSeries>();
}

static const ConstantDebugFnWrapper expr_chudnovskypiseries{
    "ChudnovskyPiSeries", spigot_chudnovsky_pi_series};

static Spigot spigot_phi()
{
    return make_unique<Phi>();
}

static const ConstantFnWrapper expr_phi{"phi", spigot_phi};

static Spigot spigot_apery()
{
    return make_unique<Apery>();
}

static const ConstantFnWrapper expr_apery{"apery", spigot_apery};

static Spigot spigot_catalan()
{
    return make_unique<Catalan>();
}

static const ConstantFnWrapper expr_catalan{"catalan", spigot_catalan};

/*
 * A class to compute pi^2/6 much faster than we can achieve by
 * computing pi and squaring it. Via the continued fraction at [1],
 * which says that
 *
 *          30
 *   pi^2 = ---------------------
 *                1^4
 *          u_1 + ---------------
 *                      2^4
 *                u_2 + ---------
 *                            3^4
 *                      u_3 + ---
 *                            ...
 *
 * where u_i = 11i^2 - 11i + 3.
 *
 * [1] https://tpiezas.wordpress.com/2012/05/04/continued-fractions-for-zeta2-and-zeta3/
 */
class PiSquaredOver6 : public Source {
    bigint i;
    int crState { -1 };

    virtual Spigot clone() override;
    virtual bool gen_interval(bigint *low, bigint_or_inf *high) override;
    virtual bool gen_matrix(Matrix &matrix) override;

  public:
    PiSquaredOver6();
};

MAKE_CLASS_GREETER(PiSquaredOver6);

PiSquaredOver6::PiSquaredOver6()
{
    dgreet();
}

Spigot PiSquaredOver6::clone()
{
    return spigot_clone(this);
}

bool PiSquaredOver6::gen_interval(bigint *low, bigint_or_inf *high)
{
    *low = 0;
    *high = bigint_or_inf::infinity;
    return false;
}

bool PiSquaredOver6::gen_matrix(Matrix &matrix)
{
    crBegin;

    /*
     * An initial matrix representing x |-> 5/x.
     */
    matrix = { 0, 5, 1, 0 };
    crReturn(false);

    /*
     * Then the regular series.
     */
    i = 1;

    while (1) {
        {
            /*
             * x |-> (11i^2-11i+3) + i^4/x
             */
            bigint i2 = i*i, i4 = i2*i2, c = 11 * (i2-i) + 3;
            matrix = { c, i4, 1, 0 };
        }
        crReturn(false);

        ++i;
    }

    crEnd;
}

Spigot spigot_pi_squared_over_6()
{
    return make_unique<PiSquaredOver6>();
}

static const ConstantDebugFnWrapper expr_pi2o6 {
    "pi2o6", spigot_pi_squared_over_6,
};

/*
 * For debugging purposes only: a constant 'Stealth1', which evaluates
 * to exactly 1, but in a way that doesn't make it read as obviously
 * rational, or make the sequence of matrices terminate.
 */
class Stealth1 : public Source {
    virtual Spigot clone() override;
    virtual bool gen_interval(bigint *low, bigint_or_inf *high) override;
    virtual bool gen_matrix(Matrix &matrix) override;

  public:
    Stealth1();
};

MAKE_CLASS_GREETER(Stealth1);

Stealth1::Stealth1()
{
    dgreet();
}

Spigot Stealth1::clone()
{
    return spigot_clone(this);
}

bool Stealth1::gen_interval(bigint *low, bigint_or_inf *high)
{
    // Start with the interval [0,2], which has the target value 1
    // dead in the middle of it.
    *low = 0;
    *high = 2;
    return true;
}

bool Stealth1::gen_matrix(Matrix &matrix)
{
    // Map the interval [0,2] to [1/2,3/2], via the affine map x |->
    // (1+x)/2.
    matrix = { 1, 1, 0, 2 };
    return false;
}

static Spigot spigot_stealth1()
{
    return make_unique<Stealth1>();
}

static const ConstantDebugFnWrapper expr_Stealth1{"Stealth1", spigot_stealth1};
