#include <stdio.h>
#include <stdlib.h>

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

/*
 * Family of integrals of trigonometric functions. Of course the
 * actual trig functions themselves - sin, cos, tan - are trivial to
 * integrate; but if you make small tweaks to turn them into things
 * like sin(x)/x and sin(x^2), you need a fresh set of special
 * functions to act as their indefinite integrals.
 *
 * All of these have just the one implementation strategy, which is to
 * take the power series of the integrand and integrate it term by
 * term. This does pretty badly for large input values, because unlike
 * the ordinary trig functions with their exact periodicity, there's
 * no useful property which you can use to range-reduce down to a nice
 * small interval on which the series converges readily. But I don't
 * know of any useful way to do better; if anyone reading this does
 * know one, please do help out!
 *
 * (Since for large x all of these functions end up spiralling round
 * and round some centre point in the (sin,cos) plane and gradually
 * tending inwards towards the centre point as x->inf, you'd like to
 * think you could do some sort of large-scale approximation based on
 * saying 'weeellll, for x in _this_ region, the current radius of the
 * twirling is about _that_, so we can approximate the function as a
 * circle of that radius about the limit point, and by finding the
 * nearest root of the integrand we can easily enough know how far
 * round that circle we are, hence we must be about _here_' and then
 * you 'just' have to find a nicely converging way to compute some
 * kind of correction term. Sadly, every treatment of that idea that
 * I've actually seen ends up talking about 'asymptotic series', i.e.
 * ones which for any given x only give you so much precision before
 * ceasing to converge; so I suspect there wouldn't in fact be a
 * correction formula which you can keep computing out to arbitrary
 * precision.)
 */

class TrigIntSeries : public Source {
    /*
     * This class computes four different power series, all obtained
     * by integrating a sin or cos power series term by term.
     *
     * The sine integral, i.e. integral of sin(x)/x. Take the series
     * for sin(x), divide by x (i.e. just shift every term down one
     * place), and integrate (i.e. shift every term back up and divide
     * it by its new index).
     *
     *        sin x     = x - x^3/3!     + x^5/5!     - x^7/7!     + ...
     *        sin x / x = 1 - x^2/3!     + x^4/5!     - x^6/7!     + ...
     *   \int sin x / x = x - x^3/(3*3!) + x^5/(5*5!) - x^7/(7*7!) + ...
     *
     * but since we'll be using this class with spigot_monotone, we
     * must make sure it's monotonic. To arrange this, we subtract off
     * the first term (effectively making it the integral of
     * sin(x)/x-1, which is always non-positive), and will add it back
     * on in post-production:
     *
     *                      - x^3/(3*3!) + x^5/(5*5!) - x^7/(7*7!) + ...
     *
     * The cosine integral, i.e. integral of cos(x)/x. Do the same to
     * the series for cos(x). But the initial constant term causes a
     * problem, which we deal with by just pretending it isn't there -
     * i.e. the series we're going to compute here will be the
     * integral of (cos(x)-1)/x, and then we can add on the integral
     * of 1/x (i.e. log x) in post-production. So that series
     * (helpfully already monotonic) is
     *
     *        (cos x - 1)     = x^2/2!     + x^4/4!     - x^6/6!     + ...
     *        (cos x - 1) / x = x  /2!     + x^3/4!     - x^5/6!     + ...
     *   \int (cos x - 1) / x = x^2/(2*2!) + x^4/(4*4!) - x^6/(6*6!) + ...
     *
     * The Fresnel sin integral, i.e. integral of sin(x^2). This time
     * we start with the series for sin(x) and space it out by
     * inserting more zero terms, then integrate _that_ term by term:
     *
     *        sin x   = x     - x^3/3!     + x^5/5!       - x^7/7!       + ...
     *        sin x^2 = x^2   - x^6/3!     + x^10/5!      - x^14/7!      + ...
     *   \int sin x^2 = x^3/3 - x^7/(7*3!) + x^11/(11*5!) - x^15/(15*7!) + ...
     *
     * Again, we need it to be monotonic; in this case, the simplest
     * thing is to put an x term back _in_ to make it so.
     *
     *             -x + x^3/3 - x^7/(7*3!) + x^11/(11*5!) - x^15/(15*7!) + ...
     *
     * The Fresnel cos integral, i.e. integral of cos(x^2). Do exactly
     * the same to cos, which introduces no further trouble:
     *
     *        cos x   = 1 - x^2/2!     + x^4/4!     - x^6/6!       + ...
     *        cos x^2 = 1 - x^4/2!     + x^8/4!     - x^12/6!      + ...
     *   \int cos x^2 = x - x^5/(5*2!) + x^9/(9*4!) - x^13/(13*6!) + ...
     *
     * and then monotonise it by removing the x term again:
     *
     *                    - x^5/(5*2!) + x^9/(9*4!) - x^13/(13*6!) + ...
     *
     * All of these power series are variants on a general schema, in
     * which each term consists of a power of x in arithmetic
     * progression (starting at x^1, x^2 or x^3 and then going up by 2
     * or 4 every time); a factorial on the denominator (starting at
     * 0! or 1!, and again going up by either 2 or 4 places each
     * time); and another denominator factor in arithmetic progression
     * (starting at 1, 2 or 3, and going up by 2 or 4 each time). And
     * they all have alternating signs. In other words, we can write
     * them all in the form
     *
     *   X/(f0*k0) - XY/(f0*f1*k1) + XY^2/(f0*f1*f2*k2) - ...
     *
     * where k0,k1,k2,... are integers in arithmetic progression,
     * f0,f1,f2 are the successive factors you multiply in to make up
     * each factorial, X is the starting power of x, and Y is the
     * power of x we multiply in each time.
     *
     * (The one exception to this schema is the anomalous -x term we
     * added in for the Fresnel sin integral. We deal with that by
     * manually adjusting its initial matrix.)
     *
     * We spigotise a power series in this format by writing X=n/d,
     * Y=N/D, and then that power series is rewritten in factorised
     * form as
     *
     *     n     1     N     1     N     1     N
     *   ---- ( -- - ---- ( -- - ---- ( -- - ---- ( ... ))))
     *   f0*d   k0   f1*D   k1   f2*D   k2   f3*D
     *
     * which gives a series of matrices looking like this:
     *
     *  ( n   0  ) ( -N*k0  f1*D    ) ( -N*k1  f2*D    ) ( -N*k2  f3*D    ) ...
     *  ( 0 f0*d ) (   0    f1*D*k0 ) (   0    f2*D*k1 ) (   0    f3*D*k2 )
     */
  public:
    enum Type { SI, CI, SF, CF };

  private:
    bigint norig, dorig;
    Type type;

    bigint n, d, N, D, i, f0, fbase, fstep, kbase, kstep;
    int crState;

  public:
    TrigIntSeries(const bigint &an, const bigint &ad, Type atype)
        : norig(an), dorig(ad), type(atype) {
        crState = -1;

        switch (type) {
          case SI:
            n = an*an*an; d = ad*ad*ad;
            N = an*an; D = ad*ad;
            kbase = 3; kstep = 2;
            f0 = -6; fbase = 3; fstep = 2;
            break;
          case CI:
            n = an*an; d = ad*ad;
            N = an*an; D = ad*ad;
            kbase = 2; kstep = 2;
            f0 = 2; fbase = 2; fstep = 2;
            break;
          case SF:
            n = an*an*an; d = ad*ad*ad;
            N = an*an*an*an; D = ad*ad*ad*ad;
            kbase = 3; kstep = 4;
            f0 = 1; fbase = 1; fstep = 2;
            break;
          case CF:
            n = an*an*an*an*an; d = ad*ad*ad*ad*ad;
            N = an*an*an*an; D = ad*ad*ad*ad;
            kbase = 5; kstep = 4;
            f0 = -2; fbase = 2; fstep = 2;
            break;
        }

        dprint("hello TrigIntSeries %b/%b %b/%b k=%b[%b] f0=%b f=%b[%b]",
               &n, &d, &N, &D, &kbase, &kstep, &f0, &fbase, &fstep);
    }

    bigint kval(const bigint &i) {
        return kbase + kstep * i;
    }

    bigint fval(const bigint &i) {
        bigint ret = 1;
        bigint base = fbase + fstep * (i-1);
        for (bigint j = 1; j <= fstep; ++j)
            ret *= ++base;
        return ret;
    }

    virtual TrigIntSeries *clone() {
        return new TrigIntSeries(norig, dorig, type);
    }

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

    bool gen_matrix(bigint *matrix) {
        crBegin;

        /* Anomalous initial matrix */
        matrix[0] = n;
        matrix[1] = 0;
        matrix[2] = 0;
        matrix[3] = f0 * d;
        if (type == SF)                // the x term
            matrix[1] = matrix[3] * -norig / dorig;
        crReturn(false);

        i = 0;
        while (1) {
            {
                bigint k = kval(i);
                dprint("k%b = %b", &i, &k);
                ++i;
                bigint f = fval(i);
                dprint("f%b = %b", &i, &f);
                matrix[0] = -N*k;
                matrix[1] = f*D;
                matrix[2] = 0;
                matrix[3] = matrix[1]*k;
            }
            crReturn(false);
        }

        crEnd;
    }
};

struct TrigIntSeriesConstructor : MonotoneConstructor {
    TrigIntSeries::Type type;
    TrigIntSeriesConstructor(TrigIntSeries::Type atype) : type(atype) {}
    MonotoneConstructor *clone() { return new TrigIntSeriesConstructor(type); }
    Spigot *construct(const bigint &n, const bigint &d) {
        return new TrigIntSeries(n, d, type);
    }
};

Spigot *spigot_Si(Spigot *x)
{
    /*
     * Si(x) is 'the' sine integral, according to Mathematica: the
     * integral of sin(x)/x, with Si(0)=0.
     *
     * The TrigIntSeries class computes the monotonic function
     * Si(x)-x, so we must add x back on now.
     */
    Spigot *series = spigot_monotone
        (new TrigIntSeriesConstructor(TrigIntSeries::SI), x->clone());
    return spigot_add(series, x);
}

Spigot *spigot_si(Spigot *x)
{
    /*
     * si(x) is just Si(x) translated so that its limit at +inf is
     * zero, rather than Si(0)=0. Turns out the difference is pi/2.
     */
    return spigot_sub(spigot_Si(x), spigot_rational_mul(spigot_pi(), 1, 2));
}

Spigot *spigot_Cin(Spigot *x)
{
    /*
     * Cin is defined by Wikipedia as the integral of (1-cos x)/x,
     * which is well defined everywhere, and hence a nice thing to
     * provide in addition to Ci. (Not to mention that it's simpler to
     * compute, being just a power series.)
     *
     * Cin is mostly monotonic already, so TrigIntSeries just computes
     * it directly. The only turning point we need to watch out for is
     * at the origin - but since it's an even function, we deal with
     * that trivially by taking |x|.
     */
    return spigot_monotone(new TrigIntSeriesConstructor(TrigIntSeries::CI),
                           spigot_abs(x));
}

Spigot *spigot_Ci(Spigot *x)
{
    /*
     * Ci is the actual integral of cos(x)/x. Since the integrand has
     * a pole at zero, we only permit positive arguments (and as far
     * as I can see, convention is _not_ to carry through to negative
     * x via the Cauchy principal value trick as Ei, li and Li do).
     * Instead we translate to make Ci(x) tend to 0 as x->inf, which
     * involves a constant term of gamma.
     */
    x = spigot_enforce(x, ENFORCE_GT, spigot_integer(0),
                       spigot_error("Ci of a non-positive number"));
    Spigot *extra = spigot_add(spigot_eulergamma(), spigot_log(x->clone()));
    return spigot_sub(extra, spigot_Cin(x));
}

Spigot *spigot_UFresnelS(Spigot *x)
{
    /*
     * UFresnel is my ugly name for the 'unnormalised' Fresnel
     * integrals, i.e. the straight-up integrals of sin(x^2) and
     * cos(x^2). Most software I've seen (Maple, Mathematica, various
     * special-function packages I found on the web for assorted
     * languages) seem to agree that the 'normalised' Fresnel
     * integrals below are the ones that deserve supporting (and
     * certainly the ones that deserve the sensible name). I expose
     * the unnormalised ones as spigot functions too, partly because
     * they're easier to compute (they're _just_ a nice power series
     * with rational coefficients) so good if you can get away with
     * using them, and mostly because I have to imagine that if you're
     * integrating something more complicated and get one of these as
     * an answer then this is the form it'll naturally drop out in.
     *
     * As above, TrigIntSeries computed UFresnelS(x)-x, so we add x
     * after spigot_monotone has finished its work.
     */
    Spigot *series = spigot_monotone
        (new TrigIntSeriesConstructor(TrigIntSeries::SF), x->clone());
    return spigot_add(series, x);
}

Spigot *spigot_UFresnelC(Spigot *x)
{
    /*
     * UFresnelC: same discussion as for UFresnelS.
     */
    Spigot *series = spigot_monotone
        (new TrigIntSeriesConstructor(TrigIntSeries::CF), x->clone());
    return spigot_add(series, x);
}

Spigot *spigot_FresnelS(Spigot *x)
{
    /*
     * The 'normalised' Fresnel integrals are the integrals of
     * sin(pi/2 x^2) and cos(pi/2 x^2), which are obtained from the
     * unnormalised version by scaling before and after, i.e.
     * FresnelS(x) = sqrt(2/pi) UFresnelS(sqrt(pi/2) x).
     *
     * All the hard work has been done in UFresnelS, so we just do
     * that scaling here in the simplest possible way.
     */
    Spigot *xt, *yt;
    xt = spigot_mul(x, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2)));
    yt = spigot_UFresnelS(xt);
    return spigot_div(yt, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2)));
}

Spigot *spigot_FresnelC(Spigot *x)
{
    /*
     * FresnelC: same discussion as for FresnelS.
     */
    Spigot *xt, *yt;
    xt = spigot_mul(x, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2)));
    yt = spigot_UFresnelC(xt);
    return spigot_div(yt, spigot_sqrt(spigot_rational_mul(spigot_pi(), 1, 2)));
}
