/*
 * exp.cpp: Exponential functions (exp, log and derived stuff).
 */

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

#include <vector>
using std::vector;

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

/*
 * We compute the exponential of a rational by translating the obvious
 * power series for exp(x) into a spigot description.
 *
 * Let x = n/d. Then we have
 *
 *                    n      n^2      n^3
 *   exp(n/d) = 1 + ---- + ------ + ------ + ...
 *                  1! d   2! d^2   3! d^3
 *
 *                    n         n         n         n
 *            = 1 + --- ( 1 + --- ( 1 + --- ( 1 + --- ( ... ) ) )
 *                  1.d       2.d       3.d       4.d
 *
 * so our matrices go
 *
 *   ( n 1.d ) ( n 2.d ) ( n 3.d ) ...
 *   ( 0 1.d ) ( 0 2.d ) ( 0 3.d )
 */
class ExpRational : public Source {
    bigint n, d, k;
    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:
    ExpRational(const bigint &an, const bigint &ad);
};

MAKE_CLASS_GREETER(ExpRational);

ExpRational::ExpRational(const bigint &an, const bigint &ad)
    : n{an}, d{ad}
{
    dgreet(n, "/", d);
}

Spigot ExpRational::clone()
{
    return spigot_clone(this, n, d);
}

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

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

    k = 1;
    while (1) {
        {
            bigint dk = d * k;
            matrix = { n, dk, 0, dk };
        }
        ++k;
        crReturn(false);
    }

    crEnd;
}

/*
 * We compute the logarithm of a rational by translating the obvious
 * power series for log(1+x) into a spigot description.
 *
 * Let x = 1 + n/d. Then we have
 *
 *                     n   2 n^2   3 n^3
 * So log(1 + n/d) = ( - - ----- + ----- - ...
 *                     d     d^2     d^3
 *
 *                     n         n       2.n
 *                   = - ( 1 - --- ( 1 - --- ( ... ) ) )
 *                     d       2.d       3.d
 *
 * so our matrices go
 *
 *   ( n 0 ) ( -n 2d ) ( -2n 3d ) ...
 *   ( 0 d ) (  0 2d ) (   0 3d )
 */
class LogRational : public Source {
    bigint in, n, d, k;
    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:
    LogRational(const bigint &an, const bigint &ad);
};

MAKE_CLASS_GREETER(LogRational);

LogRational::LogRational(const bigint &an, const bigint &ad)
    : in(an), d(ad)
{
    n = in - d; /* we're computing log(1+x), not log(x), so subtract one */
    dgreet(n, "/", d);
}

Spigot LogRational::clone()
{
    return spigot_clone(this, in, d);
}

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

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

    /*
     * The initial anomalous matrix.
     */
    matrix = { n, 0, 0, d };
    crReturn(false);

    /*
     * Then the regular series.
     */
    k = 1;
    while (1) {
        {
            bigint k_old = k;
            bigint dk = d * ++k;
            matrix = { -n * k_old, dk, 0, dk };
        }
        crReturn(false);
    }

    crEnd;
}

class ExpConstructor : public MonotoneConstructor {
    virtual Spigot construct(const bigint &n, const bigint &d) override;
};

Spigot ExpConstructor::construct(const bigint &n, const bigint &d)
{
    /*
     * Handle the special case of exp(0).
     */
    if (n == 0)
        return spigot_integer(1);

    return make_unique<ExpRational>(n, d);
}

static const MonotoneConstructorDebugWrapper<ExpConstructor> expr_Exp("Exp");

class LogConstructor : public MonotoneConstructor {
    virtual Spigot construct(const bigint &n, const bigint &d) override;
};

Spigot LogConstructor::construct(const bigint &n, const bigint &d)
{
    return make_unique<LogRational>(n, d);
}

static const MonotoneConstructorDebugWrapper<LogConstructor> expr_Log("Log");

Spigot spigot_exp(Spigot a)
{
    bigint n;

    /*
     * Range-reduce by subtracting some multiple of log(2).
     *
     * We absolutely don't want to do this _exactly_ right, by
     * rounding every number to the very nearest multiple of
     * 1/log(2): that would introduce an exactness hazard, so that
     * exp(log(2)/2) would hang without producing any output not
     * because it's fundamentally uncomputable but simply because
     * the range reducer couldn't decide which way to throw it.
     *
     * So instead, we find a rational approximation to the answer,
     * and range-reduce based on that.
     */
    {
        /*
         * Start by finding n such that n/32 is close (enough) to
         * a/log(1/2). (LogRational will converge faster for 1/2 than
         * for 2, so we do this backwards.)
         */
        StaticGenerator test(spigot_div(a->clone(),
                                        make_unique<LogRational>(1, 2)));
        n = test.get_approximate_approximant(32);
    }

    /*
     * Now divide by 32 so as to round to the nearest integer n.
     */
    n = fdiv(n + 16, 32);
    if (n != 0) {
        /* Subtract off that multiple of log(1/2). */
        a = spigot_sub(move(a), spigot_mul(spigot_integer(n),
                                           make_unique<LogRational>(1, 2)));
    }

    /*
     * Compute exp of the reduced number.
     */
    Spigot reduced = spigot_monotone(make_shared<ExpConstructor>(), move(a));

    /*
     * And now we must scale the answer by 2^-n. But first, check that
     * the result isn't out of range for an argument to bigint_power,
     * by converting the power to an unsigned and making sure it
     * didn't overflow in the process.
     */
    bigint big_abs_n = bigint_abs(n);
    unsigned abs_n = (unsigned)big_abs_n;
    if (bigint(abs_n) != big_abs_n)
        throw domain_error("cannot take exp of numbers this large");
    bigint twon = bigint_power(2, abs_n);
    if (n > 0) {
        return spigot_rational_mul(move(reduced), 1, twon);
    } else {
        return spigot_rational_mul(move(reduced), twon, 1);
    }
}

static const UnaryFnWrapper expr_exp("exp", spigot_exp);

Spigot spigot_log(Spigot a)
{
    bigint term, n;

    /*
     * Spot really easy error cases before getting into the more
     * sophisticated test below.
     */
    {
        bigint an, ad;
        if (a->is_rational(&an, &ad) && an <= 0) {
            if (an < 0)
                throw domain_error("log of negative number");
            else
                throw domain_error("log of zero");
        }
    }

    /*
     * Range-reduce by dividing by a power of two, of which we must
     * try to find approximately the right one without running into
     * any exactness hazards.
     *
     * For best convergence, we really want to get the input into the
     * range (2/3,4/3), because then we're computing log(1+x) with
     * |x|<1/3 always. But going over a little in either direction is
     * OK. So we bounce on both 96*a and 96/a until we've narrowed one
     * of their integer parts to a range that's at least _nearly_
     * within the same power of 2 and whose low end is at least
     * (about) 64; then we know what to divide by.
     */
    {
        StaticGenerator testpos(spigot_rational_mul(a->clone(), 96, 1));
        StaticGenerator testneg(spigot_mobius(a->clone(), 0, 96, 1, 0));
        bigint lo, hi;

        while (1) {
            testpos.iterate_to_bounds(&lo, &hi, nullptr, nullptr, 0, nullptr, true);
            if (hi < 0)
                throw domain_error("log of negative number");
            if (lo == 0 && hi == 0)
                throw domain_error("log of zero");
            if (lo >= 63 && (hi - lo <= 2 || (bigint_approxlog2(lo + 1) ==
                                              bigint_approxlog2(hi - 1)))) {
                n = bigint_approxlog2(hi + lo);
                // If (lo,hi) == (63,64) then we can just about hit
                // n=6, in which case simply subtracting 7 from n
                // wraps round and we'd end up trying to compute
                // 2^UINT_MAX.
                assert(n >= 6);
                if (n == 6)
                    ++n;
                n -= 7;
                a = spigot_rational_mul(move(a), 1, bigint_power(2, n));
                break;
            }

            testneg.iterate_to_bounds(&lo, &hi, nullptr, nullptr, 0, nullptr, true);
            if (hi < 0)
                throw domain_error("log of negative number");
            if (lo == 0 && hi == 0)
                throw domain_error("log of zero");
            if (lo >= 63 && (hi - lo <= 2 || (bigint_approxlog2(lo + 1) ==
                                              bigint_approxlog2(hi - 1)))) {
                n = bigint_approxlog2(hi + lo);
                // Same safety precaution as above.
                assert(n >= 6);
                if (n == 6)
                    ++n;
                n -= 7;
                a = spigot_rational_mul(move(a), bigint_power(2, n), 1);
                n = -n;
                break;
            }
        }
    }

    Spigot ret = spigot_monotone(make_shared<LogConstructor>(), move(a));
    /* Add n*log(2). (Well, subtract n*log(1/2), for faster convergence.) */
    if (n != 0)
        ret = spigot_sub(move(ret),
                         spigot_rational_mul(make_unique<LogRational>(1, 2),
                                             n, 1));
    return ret;
}

Spigot spigot_exp2(Spigot a)
{
    return spigot_pow(spigot_integer(2), move(a));
}

static const UnaryFnWrapper expr_exp2("exp2", spigot_exp2);

static Spigot spigot_exp10(Spigot a)
{
    return spigot_pow(spigot_integer(10), move(a));
}

static const UnaryFnWrapper expr_exp10("exp10", spigot_exp10);

// Given integers a,b > 1: if log_b(a) is rational, set n,d to be its
// numerator and denominator, and return true. Otherwise, return false.
static bool exact_log_z(bigint a, bigint b, bigint *n, bigint *d)
{
    bigint n0 = 1, n1 = 0;
    bigint d0 = 0, d1 = 1;

    assert(a > 1);
    assert(b > 1);

    /*
     * Algorithm is 'Euclid in the exponents'.
     *
     * If a is a rational power of b, this is equivalent to saying
     * that a = k^A and b = k^B, for some k.
     *
     *    Proof: certainly a,b must have the same _set_ of prime
     *    factors, i.e. we can write
     *         a = \prod p_i^{a_i}
     *         b = \prod p_i^{b_i}
     *    over a common set {p_i}, with all a_i,b_i > 0. Moreover,
     *    a_i/b_i must take the same value for all i. Let that value
     *    be A/B, in lowest terms (i.e. A,B coprime). Then we have
     *    Ba_i = Ab_i, i.e. A | Ba_i, but A does not divide B, so
     *    A | a_i. Similarly, B | b_i, and of course a_i/A = b_i/B. So
     *    if we define k_i = a_i/A = b_i/B for all i, then we can set
     *         k = \prod p_i^{k_i}
     *    and we have a = k^A and b = k^B as required. []
     *
     * But more than one k might satisfy that equation. (The above
     * proof constructs the _maximal_ k, but a smaller one might work,
     * e.g. if the k derived above is a square then its square root
     * would do just as well.) However, any two possible values (say,
     * k and k') will give rise to exponent pairs (A,B) and (A',B')
     * that are just scalings of each other, i.e. their ratios A/B and
     * A'/B' will be the same.
     *
     * We can find that ratio without having to know k by running
     * Euclid's algorithm, because that will yield the same sequence
     * of quotients if you adjust its inputs by a constant factor. But
     * we must modify the details a little, because we can't run
     * unmodified Euclid directly on A,B when what we have is k^A and
     * k^B for unknown k.
     *
     * So, we start by expressing Euclid in a way that does not refer
     * to 'reducing mod B', obtaining the following algorithm to find
     * the gcd of two integers A,B > 0:
     *
     *   - If A = 0, terminate.
     *   - Otherwise, find the maximal q such that B - qA >= 0.
     *   - Replace (A, B) with (B - qA, A), and repeat.
     *
     * Now all the operations we're performing on A,B are operations
     * we can still perform when what we actually have is a,b which
     * are the Ath and Bth powers of something we don't know. It's
     * clear that 'A=0' is true iff a=1; 'B - qA >= 0' is true iff
     * a^q | b; computing B - qA corresponds to computing b / a^q. So
     * we obtain *this* algorithm, which for anagram reasons surely
     * ought to go by the name 'Euclid's Logarithm':
     *
     *   - If a = 1, terminate.
     *   - Otherwise, find the maximal q such that a^q | b.
     *   - Replace (a, b) with (b / a^q, a), and repeat.
     *
     * If this algorithm succeeds, then we have found the common base
     * k such that a,b are powers of k; it will be in the variable b
     * when the algorithm terminates with a=1 (just as the ordinary
     * gcd would have been in B when the ordinary algorithm hit A=0).
     * So we could then recover the exponents A and B by repeated
     * division starting from the original a,b.
     *
     * However, there's a neater way. Recall that Euclid can be
     * augmented so that every new remainder it generates comes with a
     * pair of coefficients expressing that remainder as a linear
     * combination of the original inputs. In normal uses, you want
     * the pair of coefficients corresponding to the gcd of the
     * inputs, because that does modular inversion. But the algorithm
     * also generates the pair of coefficients corresponding to
     * _zero_, and it does it in lowest terms, i.e. running Euclid on
     * integers A,B gives you the pair (A,B) reduced to lowest terms
     * as a by-product. That's usually a bit pointless, but in this
     * case it's just what we want, because we _didn't_ know the ratio
     * A/B already, and it is exactly the output value log_b(a) that
     * we're after! So all we have to do is to build up that pair of
     * integers from the sequence of quotient values q, in just the
     * same way we would in ordinary Euclid.
     */

    while (a != 1) {
        // Find q and b/a^q simultaneously, by dividing off a as many
        // times as we can and counting how many times that was.
        bigint q;
        for (q = 0; b % a == 0; b /= a, ++q)
            /* empty loop body */;

        // Check that the resulting value of b is less than a. If the
        // algorithm has not managed to achieve this, that's the
        // failure condition: it can only happen if a,b were not in
        // fact powers of a common k at all.
        if (b >= a)
            return false;

        // Swap a,b, ready for the next iteration.
        { bigint tmp = a; a = b; b = tmp; }

        // Update our linear-combination coefficients. We only need
        // their absolute values for this application (because we know
        // our output must be a _positive_ rational).
        { bigint tmp = n0; n0 = n1; n1 = tmp + q * n1; }
        { bigint tmp = d0; d0 = d1; d1 = tmp + q * d1; }
    }

    // Done! n1/d1 is the rational we want.
    *n = n1;
    *d = d1;
    return true;
}

// Same as exact_log_z, but the inputs are rationals an/ad and bn/bd,
// expected to already be in lowest terms.
static bool exact_log_q(bigint an, bigint ad, bigint bn, bigint bd,
                        bigint *n, bigint *d)
{
    // Error cases.
    if (an < 0 || bn < 0)
        throw domain_error("log of negative number");
    if (an == 0 || bn == 0)
        throw domain_error("log of zero");
    if (bn == 1 && bd == 1)
        throw domain_error("log to base 1 is meaningless");

    // Trivial special case.
    if (an == 1 && ad == 1) {
        *n = 0;
        *d = 1;
        return true;
    }

    // Now neither input is 1, so we can know which side of 1 they're
    // on. Normalise so they're the same way up, by flipping the sign
    // of the number we'll end up returning.
    bigint sign = +1;
    if ((an > ad) != (bn > bd)) {
        bigint tmp = bn; bn = bd; bd = tmp;
        sign = -1;
    }

    // If the numerators are both 1, or the denominators are both 1,
    // then just run the integer log-finder on the other pair.

    do { /* do-while-0 just so we can 'break' - *marginally* nicer than goto */
        bool ret;
        if (ad == 1 && bd == 1) {
            ret = exact_log_z(an, bn, n, d);
        } else if (an == 1 && bn == 1) {
            ret = exact_log_z(ad, bd, n, d);
        } else {
            break;
        }

        if (ret)
            *n *= sign;
        return ret;
    } while (0);

    // We've ruled out all the ways any numerator or denominator can
    // be 1 and still give a rational answer. If none of those apply,
    // then any remaining 1 anywhere indicates failure.
    if (an == 1 || ad == 1 || bn == 1 || bd == 1)
        return false;

    // The most general case: the numerators and the denominators must
    // both have the same rational log.
    bigint dn, dd;
    if (!exact_log_z(ad, bd, &dn, &dd))
        return false;
    if (!exact_log_z(an, bn, n, d))
        return false;
    if (!(*n == dn && *d == dd))
        return false;
    *n *= sign;
    return true;
}

static Spigot spigot_log_to_base(Spigot input, Spigot base)
{
    bigint in, id, bn, bd, n, d;

    /* Spot cases where we can prove the answer is an exact rational. */
    if (input->is_rational(&in, &id) && base->is_rational(&bn, &bd) &&
        exact_log_q(in, id, bn, bd, &n, &d))
        return spigot_rational(n, d);

    /* Otherwise, log_b(a) == log(a)/log(b). */
    return spigot_div(spigot_log(move(input)), spigot_log(move(base)));
}

Spigot spigot_log2(Spigot a)
{
    return spigot_log_to_base(move(a), spigot_integer(2));
}

static const UnaryFnWrapper expr_log2("log2", spigot_log2);

static Spigot spigot_log10(Spigot a)
{
    return spigot_log_to_base(move(a), spigot_integer(10));
}

static const UnaryFnWrapper expr_log10("log10", spigot_log10);

static Spigot spigot_log_wrapper(argvector<Spigot> &args)
{
    args.syntax.enforce_trivial("log");

    /*
     * Implement operator overloading for the expression language. We
     * permit 'log' to take either one or two arguments, and invoke
     * spigot_log or spigot_log_to_base as appropriate. In C++ or
     * Python terms, this is like defining it as log(x, base=e) - the
     * second argument is optional and defaults to e if omitted.
     */
    if (args.size() == 1)
        return spigot_log(move(args[0]));
    else if (args.size() == 2)
        return spigot_log_to_base(move(args[0]), move(args[1]));
    else
        throw expr_error("expected either one or two arguments to 'log'");

}

static const VariadicFnWrapper expr_log("log", spigot_log_wrapper);

static Spigot spigot_expm1(Spigot a)
{
    return spigot_sub(spigot_exp(move(a)), spigot_integer(1));
}

static const UnaryFnWrapper expr_expm1("expm1", spigot_expm1);

Spigot spigot_log1p(Spigot a)
{
    return spigot_log(spigot_add(move(a), spigot_integer(1)));
}

static const UnaryFnWrapper expr_log1p("log1p", spigot_log1p);

static Spigot spigot_sinh(Spigot a)
{
    /*
     *           e^a - e^{-a}   e^{2a} - 1   t^2-1
     * sinh(x) = ------------ = ---------- = ----- where t=e^a
     *                2            2e^a        2t
     */
    return spigot_quadratic_ratfn(spigot_exp(move(a)), 1, 0, -1, 0, 2, 0);
}

static const UnaryFnWrapper expr_sinh("sinh", spigot_sinh);

static Spigot spigot_cosh(Spigot a)
{
    /*
     *           e^a + e^{-a}   e^{2a} + 1   t^2+1
     * cosh(x) = ------------ = ---------- = ----- where t=e^a
     *                2            2e^a        2t
     */
    return spigot_quadratic_ratfn(spigot_exp(move(a)), 1, 0, 1, 0, 2, 0);
}

static const UnaryFnWrapper expr_cosh("cosh", spigot_cosh);

static Spigot spigot_tanh(Spigot a)
{
    /*
     *           e^a - e^{-a}   e^{2a} - 1   t-1
     * tanh(x) = ------------ = ---------- = --- where t=e^{2a}
     *           e^a + e^{-a}   e^{2a} + 1   t+1
     */
    return spigot_mobius(spigot_exp(spigot_rational_mul(move(a), 2, 1)),
                         1, -1, 1, 1);
}

static const UnaryFnWrapper expr_tanh("tanh", spigot_tanh);

static Spigot spigot_asinh(Spigot a)
{
    /*
     * asinh(x) = log ( x + sqrt(x^2+1) )
     */
    Spigot a_squared_plus_1 = spigot_quadratic(a->clone(), 1, 0, 1);
    return spigot_log(
        spigot_add(move(a), spigot_sqrt(move(a_squared_plus_1))));
}

static const UnaryFnWrapper expr_asinh("asinh", spigot_asinh);

static Spigot spigot_acosh(Spigot a)
{
    /*
     * acosh(x) = log ( x + sqrt(x^2-1) )
     */
    Spigot a_squared_minus_1 = spigot_quadratic(a->clone(), 1, 0, -1);
    return spigot_log(
        spigot_add(move(a), spigot_sqrt(move(a_squared_minus_1))));
}

static const UnaryFnWrapper expr_acosh("acosh", spigot_acosh);

static Spigot spigot_atanh(Spigot a)
{
    /*
     *            1       1+x
     * atanh(x) = - log ( --- )
     *            2       1-x
     */
    return spigot_rational_mul(spigot_log(spigot_mobius(move(a), 1, 1, -1, 1)),
                               1, 2);
}

static const UnaryFnWrapper expr_atanh("atanh", spigot_atanh);

/* ----------------------------------------------------------------------
 * pow(), implemented using exp+log in the general case but with a lot
 * of faff beforehand.
 *
 * There are a lot of obvious special cases of pow which we'd like to
 * deal with directly: a rational raised to a rational power might
 * come out actually rational, in which case we'd prefer to return it
 * as an explicit rational than as an exp+log expression so that
 * further rational-consuming special cases can recognise it. Then we
 * can also convert easy things like 0^x, x^0, x^1 and x^-1 into
 * simpler computations.
 *
 * After that, you might think that a^b = exp(b*log(a)) is the
 * fallback. But not quite: there's still one thing to be careful of.
 *
 * To use the exp+log strategy, we have to start by checking the sign
 * of a, because if a<0, then we must enforce that b is a known
 * rational with an odd denominator, and use the sign of the numerator
 * to choose the output sign.
 *
 * And a sign check of a would introduce an exactness hazard: what if
 * a is non-obviously zero and b is positive, such as sin(pi)^pi? In
 * that situation, *starting* by checking the sign of a would
 * introduce an exactness hazard, whereas in fact we would like to be
 * able to keep narrowing an output interval about zero.
 *
 * So, in cases like that, we must begin generating output information
 * about a^b _before_ we find out the sign of a, and when (or if) we
 * do find it out, _then_ go and do exp+log.
 */

/*
 * This is a subfunction of pow() which deals with the general case
 * once we know we're going to have to take logs.
 *
 * It takes the sign of a as an extra input rather than finding it
 * itself; see the call site in spigot_pow() for explanation.
 */
static Spigot spigot_pow_general_case(Spigot a, Spigot b, int a_sign)
{
    if (a_sign >= 0) {
        /* a^b == a^(b*log(a)). */
        return spigot_exp(spigot_mul(move(b), spigot_log(move(a))));
    } else {
        bigint bn, bd;

        if (!b->is_rational(&bn, &bd)) {
            throw domain_error("negative number raised to"
                               " not-obviously-rational power");
        }
        while (bn % 2U == 0 && bd % 2U == 0) {
            bn /= 2;
            bd /= 2;
        }
        if (bd % 2U == 0) {
            throw domain_error("negative number raised to"
                               " power with even denominator");
        }
        a = spigot_rational_mul(move(a), -1, 1);
        Spigot absret = spigot_exp(spigot_mul(move(b), spigot_log(move(a))));
        if (bn % 2U == 0)
            return absret;
        else
            return spigot_neg(move(absret));
    }
}

/*
 * This class deals with the case where we had to defer the sign check
 * of a. It narrows an output interval conservatively about zero for
 * as long as we haven't distinguished a from 0 yet, and then if we
 * later do find out a's sign, it switches to returning the output of
 * spigot_pow_general_case.
 */
class PowPrefixWrapper : public BinaryIntervalSource {
    Spigot a, b;
    bigint xn;
    bool reciprocal;
    int a_sign { 0 };
    unique_ptr<BracketingGenerator> bg;
    int crState { -1 };

    virtual Spigot clone() override;
    virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi,
                                  unsigned *ret_bits) override;

  public:
    PowPrefixWrapper(Spigot aa, Spigot ab, bigint axn, bool areciprocal);
};

MAKE_CLASS_GREETER(PowPrefixWrapper);

PowPrefixWrapper::PowPrefixWrapper(Spigot aa, Spigot ab,
                                   bigint axn, bool areciprocal)
    : a(move(aa)), b(move(ab)), xn(axn), reciprocal(areciprocal)
{
    dgreet(a->dbg_id(), " ", b->dbg_id(), " ", xn, " ", reciprocal);
}

Spigot PowPrefixWrapper::clone()
{
    return spigot_clone(this, a->clone(), b->clone(), xn, reciprocal);
}

void PowPrefixWrapper::gen_bin_interval(bigint *ret_lo, bigint *ret_hi,
                                        unsigned *ret_bits)
{
    crBegin;

    /*
     * Initially, we set up a BracketingGenerator for just a.
     */
    bg = make_unique<BracketingGenerator>(a->clone());
    while (1) {
        /*
         * Fetch some information about a, and see what we can do
         * with it.
         */
        {
            bigint nlo, nhi;
            unsigned dbits;
            bg->get_bracket_shift(&nlo, &nhi, &dbits);
            dprint("input bracket for a: (", nlo, ",", nhi, ") / 2^", dbits);

            /*
             * If the interval tells us the sign of a, then we can
             * leave this preliminary loop and go to sensible
             * exp+log evaluation.
             */
            if (nlo > 0) {
                dprint("a > 0");
                a_sign = +1;
                break;
            } else if (nhi < 0) {
                dprint("a < 0");
                a_sign = -1;
                break;
            }

            /*
             * Otherwise, approximate this interval by expanding
             * it to one of the form [-2^k,+2^k], and then turn
             * that into an output interval.
             */
            int k, k2;

            k = -(int)dbits + bigint_approxlog2(bigint_abs(nlo)) + 1;
            k2 = -(int)dbits + bigint_approxlog2(bigint_abs(nhi)) + 1;
            if (k < k2)
                k = k2;
            dprint("expanded input bracket for a: [-2^", k, ", +2^", k, "]");

            /*
             * We have a in [-2^k, +2^k]. Compute a bounding
             * interval for a^b using xn and reciprocal.
             */
            if (reciprocal) {
                k = fdiv(k + xn - 1, xn);
            } else {
                k = k * xn;
            }

            /*
             * Now we know a^b in [-2^k, +2^k], so we can safely
             * return that.
             */
            *ret_lo = -1;
            *ret_hi = +1;
            *ret_bits = abs(k);
        }
        dprint("output bracket for a^b: (",
               *ret_lo, ",", *ret_hi, ") / 2^", *ret_bits);

        crReturnV;
    }

    /*
     * If we've broken out of this loop, then we now know the sign
     * of a, so we can switch over to returning binary intervals
     * from that.
     *
     * The implementation of BinaryIntervalSource will take care
     * of us potentially not passing a _nested_ sequence of
     * intervals, so we've got no need to deal with any fiddly
     * special cases in that area.
     */
    bg = make_unique<BracketingGenerator>(
        spigot_pow_general_case(a->clone(), b->clone(), a_sign));
    while (1) {
        bg->get_bracket_shift(ret_lo, ret_hi, ret_bits);
        dprint("passthrough bracket for a^b: (",
               *ret_lo, ",", *ret_hi, ") / 2^", *ret_bits);
        crReturnV;
    }

    crEnd;
}

Spigot spigot_pow(Spigot a, Spigot b)
{
    bigint an, ad, bn, bd;
    bool a_rat, b_rat;

    /*
     * Start with the special-case of _both_ inputs being rational
     * (and within a range we can handle, and a is not zero, which
     * this handler wouldn't like anyway).
     */
    a_rat = a->is_rational(&an, &ad);
    b_rat = b->is_rational(&bn, &bd);
    if (a_rat && b_rat && an != 0 && bn != 0 && an != ad) {
        /*
         * Special case we can handle easily, at least if the numbers
         * aren't too excessive: a rational raised to a rational
         * power. We deal with the numerator of b by simply raising an
         * and ad to that power using integer exponentiation; then, if
         * b has a nontrivial denominator, we use spigot_algebraic to
         * take a root of it.
         *
         * Raising an integer to a power is nice and easy, and we
         * permit it as long as the output number isn't too excessive
         * (determined by the constant 'pow_bits_limit'). The
         * root-taking via spigot_algebraic is more costly, and we
         * have a smaller limit for that, after which it's cheaper to
         * use exp+log anyway.
         */
        const int pow_bits_limit = 1000000;
        const int alg_degree_limit = 100;

        bigint an_abs = bigint_abs(an);
        bigint ad_abs = bigint_abs(ad);
        bigint bn_abs = bigint_abs(bn);
        bigint bd_abs = bigint_abs(bd);

        bool bn_ok = (bigint_approxlog2(an_abs) * bn_abs < pow_bits_limit &&
                      bigint_approxlog2(ad_abs) * bn_abs < pow_bits_limit);
        bool bd_ok = (bd_abs < alg_degree_limit);

        if (bn_ok && bd_ok) {
            bigint abs_n = bigint_power(an_abs, bn_abs);
            bigint abs_d = bigint_power(ad_abs, bn_abs);
            if (an < 0 && bd % 2U == 0) {
                throw domain_error("negative number raised to"
                                   " power with even denominator");
            }
            int sign = (an < 0 && (bn_abs % 2U) != 0) ? -1 : +1;
            if (bd == 1) {
                if (bn >= 0)
                    return spigot_rational(sign * abs_n, abs_d);
                else
                    return spigot_rational(sign * abs_d, abs_n);
            } else {
                vector<bigint> P;
                P.push_back(sign * (bn >= 0 ? abs_n : abs_d));
                P.resize((unsigned)bd, 0);
                P.push_back(-(bn >= 0 ? abs_d : abs_n));
                return spigot_algebraic(P, 0, P[0], 1);
            }
        }
    }

    /*
     * A few other special cases where one of a,b has a particular
     * rational value (but the other doesn't have to).
     */
    if (a_rat && an == 0) {
        /*
         * 0^positive = 0, 0^0 and 0^negative are errors.
         */
        if (get_sign(move(b)) > 0)
            return spigot_integer(0);
        else
            throw domain_error("zero raised to non-positive power");
    }
    if (b_rat && bn == 0) {
        /*
         * Apart from the above case, anything^0 = 1.
         */
        return spigot_integer(1);
    }
    if (a_rat && an == 1*ad) {
        /*
         * So is 1^anything.
         */
        return spigot_integer(1);
    }
    if (b_rat && bn == 1*bd) {
        /*
         * Anything^1 is just itself.
         */
        return a;
    }
    if (b_rat && bn == -1*bd) {
        /*
         * Anything^-1 is its reciprocal.
         */
        return spigot_reciprocal(move(a));
    }
    if (b_rat && bn == 2*bd) {
        /*
         * For anything^2, we have a nice fast spigot_square.
         */
        return spigot_square(move(a));
    }
    if (b_rat && bn == -2*bd) {
        /*
         * Ditto invsquare.
         */
        return spigot_invsquare(move(a));
    }

    /*
     * Now we're nearly ready to do exp+log, modulo a sign check of a,
     * and (as explained above) that's hazardous to do in the obvious
     * way. Instead we do some complicated faffing about to find out
     * _either_ the sign of a _or_ narrow down to a special case in
     * which we can use the PowPrefixWrapper class.
     */

    /* pow has a discontinuity at (0,0), so a _parallel_ sign test of
     * a,b does not introduce any avoidable exactness hazard */
    int s = parallel_sign_test(a->clone(), b->clone());
    if (s == +1 || s == -1) {
        /*
         * The parallel sign test has delivered us the sign of a, so
         * we can go straight to the general case.
         */
        return spigot_pow_general_case(move(a), move(b), s);
    }

    if (s == -2) {
        /*
         * If b < 0, then a |-> pow(a,b) is discontinuous at 0, so we
         * can't avoid a hazard anyway at a=0, and hence can safely
         * test the sign of a without introducing any further hazards.
         */
        return spigot_pow_general_case(move(a), move(b), get_sign(a->clone()));
    }

    /*
     * The only remaining case is s == +2, i.e. we know b > 0 and we
     * don't yet know about the sign of a.
     *
     * This is _potentially_ our special case. But it'll be much
     * easier if we know that |a| < 1, and if that's not the case then
     * finding the sign of a will be unproblematic anyway. So let's
     * start by narrowing down to _that_ case.
     */
    {
        StaticGenerator sg(a->clone());
        bigint approx = sg.get_approximate_approximant(8);
        if (bigint_abs(approx) > 4) {
            /*
             * We know the sign of a.
             */
            return spigot_pow_general_case(move(a), move(b),
                                           (approx > 0) ? +1 : -1);
        }
        /*
         * Otherwise, we know |a| < 1.
         */
    }

    /*
     * Now we know that b > 0 and |a| < 1, so this really is
     * the special case we want to take care with.
     *
     * Next, find a nice lower bound on b, i.e. a number 0 < x < b
     * such that it's easy to compute an upper bound on epsilon^x for
     * epsilon a power of two. I think making x an integer or the
     * reciprocal of an integer should be nice enough.
     */
    bool reciprocal;                   // if true, x=1/xn, else x=xn
    bigint xn;
    {
        StaticGenerator sg(b->clone());
        bigint approx = sg.get_approximate_approximant(32);
        if (approx > 36) {
            // b is big enough that we can take x an integer
            reciprocal = false;
            xn = fdiv(approx - 4, 32);
        } else {
            // Otherwise, find an integer _upper_ bound on 1/b, and
            // use the reciprocal of that
            StaticGenerator sg2(spigot_reciprocal(b->clone()));
            approx = sg2.get_approximate_approximant(32);
            reciprocal = true;
            xn = fdiv(approx + 4 + 31, 32);
        }
    }

    return make_unique<PowPrefixWrapper>(move(a), move(b), xn, reciprocal);
}

static const BinaryFnWrapper expr_pow("pow", spigot_pow);
