#include <valarray>
#include <iterator>

#include <symengine/prime_sieve.h>
#include <symengine/ntheory.h>
#include <symengine/rational.h>
#include <symengine/add.h>
#include <symengine/mul.h>
#include <symengine/pow.h>
#ifdef HAVE_SYMENGINE_ECM
#include <ecm.h>
#endif // HAVE_SYMENGINE_ECM
#ifdef HAVE_SYMENGINE_PRIMESIEVE
#include <primesieve.hpp>
#endif // HAVE_SYMENGINE_PRIMESIEVE
#ifdef HAVE_SYMENGINE_ARB
#include "arb.h"
#include "bernoulli.h"
#include "rational.h"
#endif // HAVE_SYMENGINE_ARB
#ifndef HAVE_SYMENGINE_GMP
#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random.hpp>
#endif // !HAVE_SYMENGINE_GMP

namespace SymEngine
{

// Basic number theoretic functions
RCP<const Integer> gcd(const Integer &a, const Integer &b)
{
    integer_class g;
    mp_gcd(g, a.as_integer_class(), b.as_integer_class());
    return integer(std::move(g));
}

void gcd_ext(const Ptr<RCP<const Integer>> &g, const Ptr<RCP<const Integer>> &s,
             const Ptr<RCP<const Integer>> &t, const Integer &a,
             const Integer &b)
{
    integer_class g_, s_, t_;
    mp_gcdext(g_, s_, t_, a.as_integer_class(), b.as_integer_class());
    *g = integer(std::move(g_));
    *s = integer(std::move(s_));
    *t = integer(std::move(t_));
}

RCP<const Integer> lcm(const Integer &a, const Integer &b)
{
    integer_class c;
    mp_lcm(c, a.as_integer_class(), b.as_integer_class());
    return integer(std::move(c));
}

int mod_inverse(const Ptr<RCP<const Integer>> &b, const Integer &a,
                const Integer &m)
{
    int ret_val;
    integer_class inv_t;
    ret_val = mp_invert(inv_t, a.as_integer_class(), m.as_integer_class());
    *b = integer(std::move(inv_t));
    return ret_val;
}

RCP<const Integer> mod(const Integer &n, const Integer &d)
{
    return integer(n.as_integer_class() % d.as_integer_class());
}

RCP<const Integer> quotient(const Integer &n, const Integer &d)
{
    return integer(n.as_integer_class() / d.as_integer_class());
}

void quotient_mod(const Ptr<RCP<const Integer>> &q,
                  const Ptr<RCP<const Integer>> &r, const Integer &n,
                  const Integer &d)
{
    integer_class _q, _r;
    mp_tdiv_qr(_q, _r, n.as_integer_class(), d.as_integer_class());
    *q = integer(std::move(_q));
    *r = integer(std::move(_r));
}

RCP<const Integer> mod_f(const Integer &n, const Integer &d)
{
    integer_class q;
    mp_fdiv_r(q, n.as_integer_class(), d.as_integer_class());
    return integer(std::move(q));
}

RCP<const Integer> quotient_f(const Integer &n, const Integer &d)
{
    integer_class q;
    mp_fdiv_q(q, n.as_integer_class(), d.as_integer_class());
    return integer(std::move(q));
}

void quotient_mod_f(const Ptr<RCP<const Integer>> &q,
                    const Ptr<RCP<const Integer>> &r, const Integer &n,
                    const Integer &d)
{
    integer_class _q, _r;
    mp_fdiv_qr(_q, _r, n.as_integer_class(), d.as_integer_class());
    *q = integer(std::move(_q));
    *r = integer(std::move(_r));
}

RCP<const Integer> fibonacci(unsigned long n)
{
    integer_class f;
    mp_fib_ui(f, n);
    return integer(std::move(f));
}

void fibonacci2(const Ptr<RCP<const Integer>> &g,
                const Ptr<RCP<const Integer>> &s, unsigned long n)
{
    integer_class g_t;
    integer_class s_t;
    mp_fib2_ui(g_t, s_t, n);
    *g = integer(std::move(g_t));
    *s = integer(std::move(s_t));
}

RCP<const Integer> lucas(unsigned long n)
{
    integer_class f;
    mp_lucnum_ui(f, n);
    return integer(std::move(f));
}

void lucas2(const Ptr<RCP<const Integer>> &g, const Ptr<RCP<const Integer>> &s,
            unsigned long n)
{
    integer_class g_t;
    integer_class s_t;
    mp_lucnum2_ui(g_t, s_t, n);
    *g = integer(std::move(g_t));
    *s = integer(std::move(s_t));
}

// Binomial Coefficient
RCP<const Integer> binomial(const Integer &n, unsigned long k)
{
    integer_class f;
    mp_bin_ui(f, n.as_integer_class(), k);
    return integer(std::move(f));
}

// Factorial
RCP<const Integer> factorial(unsigned long n)
{
    integer_class f;
    mp_fac_ui(f, n);
    return integer(std::move(f));
}

// Returns true if `b` divides `a` without reminder
bool divides(const Integer &a, const Integer &b)
{
    return mp_divisible_p(a.as_integer_class(), b.as_integer_class()) != 0;
}

// Prime functions
int probab_prime_p(const Integer &a, unsigned reps)
{
    return mp_probab_prime_p(a.as_integer_class(), reps);
}

RCP<const Integer> nextprime(const Integer &a)
{
    integer_class c;
    mp_nextprime(c, a.as_integer_class());
    return integer(std::move(c));
}

namespace
{
// Factoring by Trial division using primes only
int _factor_trial_division_sieve(integer_class &factor, const integer_class &N)
{
    integer_class sqrtN = mp_sqrt(N);
    unsigned long limit = mp_get_ui(sqrtN);
    if (limit > std::numeric_limits<unsigned>::max())
        throw SymEngineException("N too large to factor");
    Sieve::iterator pi(numeric_cast<unsigned>(limit));
    unsigned p;
    while ((p = pi.next_prime()) <= limit) {
        if (N % p == 0) {
            factor = p;
            return 1;
        }
    }
    return 0;
}
// Factor using lehman method.
int _factor_lehman_method(integer_class &rop, const integer_class &n)
{
    if (n < 21)
        throw SymEngineException("Require n >= 21 to use lehman method");

    int ret_val = 0;
    integer_class u_bound;

    mp_root(u_bound, n, 3);
    u_bound = u_bound + 1;

    Sieve::iterator pi(numeric_cast<unsigned>(mp_get_ui(u_bound)));
    unsigned p;
    while ((p = pi.next_prime()) <= mp_get_ui(u_bound)) {
        if (n % p == 0) {
            rop = n / p;
            ret_val = 1;
            break;
        }
    }

    if (not ret_val) {

        integer_class k, a, b, l;

        k = 1;

        while (k <= u_bound) {
            a = mp_sqrt(4 * k * n);
            mp_root(b, n, 6);
            mp_root(l, k, 2);
            b = b / (4 * l);
            b = b + a;

            while (a <= b) {
                l = a * a - 4 * k * n;
                if (mp_perfect_square_p(l)) {
                    b = a + mp_sqrt(l);
                    mp_gcd(rop, n, b);
                    ret_val = 1;
                    break;
                }
                a = a + 1;
            }
            if (ret_val)
                break;
            k = k + 1;
        }
    }

    return ret_val;
}
} // anonymous namespace

int factor_lehman_method(const Ptr<RCP<const Integer>> &f, const Integer &n)
{
    int ret_val;
    integer_class rop;

    ret_val = _factor_lehman_method(rop, n.as_integer_class());
    *f = integer(std::move(rop));
    return ret_val;
}

namespace
{
// Factor using Pollard's p-1 method
int _factor_pollard_pm1_method(integer_class &rop, const integer_class &n,
                               const integer_class &c, unsigned B)
{
    if (n < 4 or B < 3)
        throw SymEngineException(
            "Require n > 3 and B > 2 to use Pollard's p-1 method");

    integer_class m, _c;
    _c = c;

    Sieve::iterator pi(B);
    unsigned p;
    while ((p = pi.next_prime()) <= B) {
        m = 1;
        // calculate log(p, B), this can be improved
        while (m <= B / p) {
            m = m * p;
        }
        mp_powm(_c, _c, m, n);
    }
    _c = _c - 1;
    mp_gcd(rop, _c, n);

    if (rop == 1 or rop == n)
        return 0;
    else
        return 1;
}
} // anonymous namespace

int factor_pollard_pm1_method(const Ptr<RCP<const Integer>> &f,
                              const Integer &n, unsigned B, unsigned retries)
{
    int ret_val = 0;
    integer_class rop, nm4, c;

    mp_randstate state;
    nm4 = n.as_integer_class() - 4;

    for (unsigned i = 0; i < retries and ret_val == 0; ++i) {
        state.urandomint(c, nm4);
        c += 2;
        ret_val = _factor_pollard_pm1_method(rop, n.as_integer_class(), c, B);
    }

    if (ret_val != 0)
        *f = integer(std::move(rop));
    return ret_val;
}

namespace
{
// Factor using Pollard's rho method
int _factor_pollard_rho_method(integer_class &rop, const integer_class &n,
                               const integer_class &a, const integer_class &s,
                               unsigned steps = 10000)
{
    if (n < 5)
        throw SymEngineException("Require n > 4 to use pollard's-rho method");

    integer_class u, v, g, m;
    u = s;
    v = s;

    for (unsigned i = 0; i < steps; ++i) {
        u = (u * u + a) % n;
        v = (v * v + a) % n;
        v = (v * v + a) % n;
        m = u - v;
        mp_gcd(g, m, n);

        if (g == n)
            return 0;
        if (g == 1)
            continue;
        rop = g;
        return 1;
    }
    return 0;
}
} // namespace

int factor_pollard_rho_method(const Ptr<RCP<const Integer>> &f,
                              const Integer &n, unsigned retries)
{
    int ret_val = 0;
    integer_class rop, nm1, nm4, a, s;
    mp_randstate state;
    nm1 = n.as_integer_class() - 1;
    nm4 = n.as_integer_class() - 4;

    for (unsigned i = 0; i < retries and ret_val == 0; ++i) {
        state.urandomint(a, nm1);
        state.urandomint(s, nm4);
        s += 1;
        ret_val = _factor_pollard_rho_method(rop, n.as_integer_class(), a, s);
    }

    if (ret_val != 0)
        *f = integer(std::move(rop));
    return ret_val;
}

// Factorization
int factor(const Ptr<RCP<const Integer>> &f, const Integer &n, double B1)
{
    int ret_val = 0;
    integer_class _n, _f;

    _n = n.as_integer_class();

#ifdef HAVE_SYMENGINE_ECM
    if (mp_perfect_power_p(_n)) {

        unsigned long int i = 1;
        integer_class m, rem;
        rem = 1; // Any non zero number
        m = 2;   // set `m` to 2**i, i = 1 at the begining

        // calculate log2n, this can be improved
        for (; m < _n; ++i)
            m = m * 2;

        // eventually `rem` = 0 zero as `n` is a perfect power. `f_t` will
        // be set to a factor of `n` when that happens
        while (i > 1 and rem != 0) {
            mp_rootrem(_f, rem, _n, i);
            --i;
        }

        ret_val = 1;
    } else {

        if (mp_probab_prime_p(_n, 25) > 0) { // most probably, n is a prime
            ret_val = 0;
            _f = _n;
        } else {

            for (int i = 0; i < 10 and not ret_val; ++i)
                ret_val = ecm_factor(get_mpz_t(_f), get_mpz_t(_n), B1, nullptr);
            mp_demote(_f);
            if (not ret_val)
                throw SymEngineException(
                    "ECM failed to factor the given number");
        }
    }
#else
    // B1 is discarded if gmp-ecm is not installed
    ret_val = _factor_trial_division_sieve(_f, _n);
#endif // HAVE_SYMENGINE_ECM
    *f = integer(std::move(_f));

    return ret_val;
}

int factor_trial_division(const Ptr<RCP<const Integer>> &f, const Integer &n)
{
    int ret_val;
    integer_class factor;
    ret_val = _factor_trial_division_sieve(factor, n.as_integer_class());
    if (ret_val == 1)
        *f = integer(std::move(factor));
    return ret_val;
}

void prime_factors(std::vector<RCP<const Integer>> &prime_list,
                   const Integer &n)
{
    integer_class sqrtN;
    integer_class _n = n.as_integer_class();
    if (_n == 0)
        return;
    if (_n < 0)
        _n *= -1;

    sqrtN = mp_sqrt(_n);
    auto limit = mp_get_ui(sqrtN);
    if (not mp_fits_ulong_p(sqrtN)
        or limit > std::numeric_limits<unsigned>::max())
        throw SymEngineException("N too large to factor");
    Sieve::iterator pi(numeric_cast<unsigned>(limit));
    unsigned p;

    while ((p = pi.next_prime()) <= limit) {
        while (_n % p == 0) {
            prime_list.push_back(integer(p));
            _n = _n / p;
        }
        if (_n == 1)
            break;
    }
    if (not(_n == 1))
        prime_list.push_back(integer(std::move(_n)));
}

void prime_factor_multiplicities(map_integer_uint &primes_mul, const Integer &n)
{
    integer_class sqrtN;
    integer_class _n = n.as_integer_class();
    unsigned count;
    if (_n == 0)
        return;
    if (_n < 0)
        _n *= -1;

    sqrtN = mp_sqrt(_n);
    auto limit = mp_get_ui(sqrtN);
    if (not mp_fits_ulong_p(sqrtN)
        or limit > std::numeric_limits<unsigned>::max())
        throw SymEngineException("N too large to factor");
    Sieve::iterator pi(numeric_cast<unsigned>(limit));

    unsigned p;
    while ((p = pi.next_prime()) <= limit) {
        count = 0;
        while (_n % p == 0) { // when a prime factor is found, we divide
            ++count;          // _n by that prime as much as we can
            _n = _n / p;
        }
        if (count > 0) {
            insert(primes_mul, integer(p), count);
            if (_n == 1)
                break;
        }
    }
    if (not(_n == 1))
        insert(primes_mul, integer(std::move(_n)), 1);
}

RCP<const Number> bernoulli(unsigned long n)
{
#ifdef HAVE_SYMENGINE_ARB
    fmpq_t res;
    fmpq_init(res);
    bernoulli_fmpq_ui(res, n);
    mpq_t a;
    mpq_init(a);
    fmpq_get_mpq(a, res);
    rational_class b(a);
    fmpq_clear(res);
    mpq_clear(a);
    return Rational::from_mpq(std::move(b));
#else
    // TODO: implement a faster algorithm
    std::vector<rational_class> v(n + 1);
    for (unsigned m = 0; m <= n; ++m) {
        v[m] = rational_class(1u, m + 1);

        for (unsigned j = m; j >= 1; --j) {
            v[j - 1] = j * (v[j - 1] - v[j]);
        }
    }
    return Rational::from_mpq(v[0]);
#endif
}

RCP<const Number> harmonic(unsigned long n, long m)
{
    rational_class res(0);
    if (m == 1) {
        for (unsigned i = 1; i <= n; ++i) {
            res += rational_class(1u, i);
        }
        return Rational::from_mpq(res);
    } else {
        for (unsigned i = 1; i <= n; ++i) {
            if (m > 0) {
                rational_class t(1u, i);
#if SYMENGINE_INTEGER_CLASS != SYMENGINE_BOOSTMP
                mp_pow_ui(get_den(t), get_den(t), m);
#else
                mp_pow_ui(t, t, m);
#endif
                res += t;
            } else {
                integer_class t(i);
                mp_pow_ui(t, t, static_cast<unsigned long>(-m));
                res += t;
            }
        }
        return Rational::from_mpq(res);
    }
}

// References : Cohen H., A course in computational algebraic number theory
// (1996), page 21.
bool crt(const Ptr<RCP<const Integer>> &R,
         const std::vector<RCP<const Integer>> &rem,
         const std::vector<RCP<const Integer>> &mod)
{
    if (mod.size() > rem.size())
        throw SymEngineException("Too few remainders");
    if (mod.size() == 0)
        throw SymEngineException("Moduli vector cannot be empty");

    integer_class m, r, g, s, t;
    m = mod[0]->as_integer_class();
    r = rem[0]->as_integer_class();

    for (unsigned i = 1; i < mod.size(); ++i) {
        mp_gcdext(g, s, t, m, mod[i]->as_integer_class());
        // g = s * m + t * mod[i]
        t = rem[i]->as_integer_class() - r;
        if (not mp_divisible_p(t, g))
            return false;
        r += m * s * (t / g); // r += m * (m**-1 mod[i]/g)* (rem[i] - r) / g
        m *= mod[i]->as_integer_class() / g;
        mp_fdiv_r(r, r, m);
    }
    *R = integer(std::move(r));
    return true;
}

namespace
{
// Crt over a cartesian product of vectors (Assuming that moduli are pairwise
// relatively prime).
void _crt_cartesian(std::vector<RCP<const Integer>> &R,
                    const std::vector<std::vector<RCP<const Integer>>> &rem,
                    const std::vector<RCP<const Integer>> &mod)
{
    if (mod.size() > rem.size())
        throw SymEngineException("Too few remainders");
    if (mod.size() == 0)
        throw SymEngineException("Moduli vector cannot be empty");
    integer_class m, _m, r, s, t;
    m = mod[0]->as_integer_class();
    R = rem[0];

    for (unsigned i = 1; i < mod.size(); ++i) {
        std::vector<RCP<const Integer>> rem2;
        mp_invert(s, m, mod[i]->as_integer_class());
        _m = m;
        m *= mod[i]->as_integer_class();
        for (auto &elem : R) {
            for (auto &_k : rem[i]) {
                r = elem->as_integer_class();
                r += _m * s * (_k->as_integer_class() - r);
                mp_fdiv_r(r, r, m);
                rem2.push_back(integer(r));
            }
        }
        R = rem2;
    }
}

// Tests whether n is a prime power and finds a prime p and e such that n =
// p**e.
bool _prime_power(integer_class &p, integer_class &e, const integer_class &n)
{
    if (n < 2)
        return false;
    integer_class _n = n, temp;
    e = 1;
    unsigned i = 2;
    while (mp_perfect_power_p(_n) and _n >= 2) {
        if (mp_root(temp, _n, i)) {
            e *= i;
            _n = temp;
        } else {
            ++i;
        }
    }
    if (mp_probab_prime_p(_n, 25)) {
        p = _n;
        return true;
    }
    return false;
}

// Computes a primitive root modulo p**e or 2*p**e where p is an odd prime.
// References : Cohen H., A course in computational algebraic number theory
// (2009), pages 25-27.
void _primitive_root(integer_class &g, const integer_class &p,
                     const integer_class &e, bool even = false)
{
    std::vector<RCP<const Integer>> primes;
    prime_factors(primes, *integer(p - 1));

    integer_class t;
    g = 2;
    while (g < p) {
        bool root = true;
        for (const auto &it : primes) {
            t = it->as_integer_class();
            t = (p - 1) / t;
            mp_powm(t, g, t, p);
            if (t == 1) { // If g**(p-1)/q is 1 then g is not a primitive root.
                root = false;
                break;
            }
        }
        if (root)
            break;
        ++g;
    }

    if (e > 1) {
        t = p * p;
        integer_class pm1 = p - 1;
        mp_powm(t, g, pm1, t);
        if (t == 1) { // If g**(p-1) mod (p**2) == 1 then g + p is a primitive
                      // root.
            g += p;
        }
    }
    if (even and g % 2 == 0) {
        mp_pow_ui(t, p, mp_get_ui(e));
        g += t; // If g is even then root of 2*p**e is g + p**e.
    }
}

} // anonymous namespace

bool primitive_root(const Ptr<RCP<const Integer>> &g, const Integer &n)
{
    integer_class _n = n.as_integer_class();
    if (_n < 0)
        _n = -_n;
    if (_n <= 1)
        return false;
    if (_n < 5) {
        *g = integer(_n - 1);
        return true;
    }
    bool even = false;
    if (_n % 2 == 0) {
        if (_n % 4 == 0) {
            return false; // If n mod 4 == 0 and n > 4, then no primitive roots.
        }
        _n /= 2;
        even = true;
    }
    integer_class p, e;
    if (not _prime_power(p, e, _n))
        return false;
    _primitive_root(_n, p, e, even);
    *g = integer(std::move(_n));
    return true;
}

namespace
{
// Computes primitive roots modulo p**e or 2*p**e where p is an odd prime.
// References :
// [1] Cohen H., A course in computational algebraic number theory (1996), pages
// 25-27.
// [2] Hackman P., Elementary number theory (2009), page 28.
void _primitive_root_list(std::vector<RCP<const Integer>> &roots,
                          const integer_class &p, const integer_class &e,
                          bool even = false)
{
    integer_class g, h, d, t, pe2, n, pm1;
    _primitive_root(g, p, integer_class(1),
                    false); // Find one primitive root for p.
    h = 1;
    pm1 = p - 1;
    // Generate other primitive roots for p. h = g**i and gcd(i, p-1) = 1.
    // Ref[2]
    mp_pow_ui(n, p, mp_get_ui(e));
    for (unsigned long i = 1; i < p; ++i) {
        h *= g;
        h %= p;
        mp_gcd(d, pm1, integer_class(i));
        if (d == 1) {
            if (e == 1) {
                if (even and h % 2 == 0)
                    roots.push_back(integer(h + n));
                else
                    roots.push_back(integer(h));
            } else {
                integer_class pp = p * p;
                // Find d such that (h + d*p)**(p-1) mod (p**2) == 1. Ref[1]
                // h**(p-1) - 1 = d*p*h**(p-2)
                // d = (h - h**(2-p)) / p
                t = 2 - p;
                mp_powm(d, h, t, pp);
                d = ((h - d) / p + p) % p;
                t = h;
                // t = h + i * p + j * p * p and i != d
                mp_pow_ui(pe2, p, mp_get_ui(e) - 2);
                for (unsigned long j = 0; j < pe2; ++j) {
                    for (unsigned long i = 0; i < p; ++i) {
                        if (i != d) {
                            if (even and t % 2 == 0)
                                roots.push_back(integer(t + n));
                            else
                                roots.push_back(integer(t));
                        }
                        t += p;
                    }
                }
            }
        }
    }
} //_primitive_root_list
} // anonymous namespace

void primitive_root_list(std::vector<RCP<const Integer>> &roots,
                         const Integer &n)
{
    integer_class _n = n.as_integer_class();
    if (_n < 0)
        _n = -_n;
    if (_n <= 1)
        return;
    if (_n < 5) {
        roots.push_back(integer(_n - 1));
        return;
    }
    bool even = false;
    if (_n % 2 == 0) {
        if (_n % 4 == 0) {
            return; // If n%4 == 0 and n > 4, then no primitive roots.
        }
        _n /= 2;
        even = true;
    }
    integer_class p, e;
    if (not _prime_power(p, e, _n))
        return;
    _primitive_root_list(roots, p, e, even);
    std::sort(roots.begin(), roots.end(), SymEngine::RCPIntegerKeyLess());
    return;
}

RCP<const Integer> totient(const RCP<const Integer> &n)
{
    if (n->is_zero())
        return integer(1);

    integer_class phi = n->as_integer_class(), p;
    if (phi < 0)
        phi = -phi;
    map_integer_uint prime_mul;
    prime_factor_multiplicities(prime_mul, *n);

    for (const auto &it : prime_mul) {
        p = it.first->as_integer_class();
        mp_divexact(phi, phi, p);
        // phi is exactly divisible by p.
        phi *= p - 1;
    }
    return integer(std::move(phi));
}

RCP<const Integer> carmichael(const RCP<const Integer> &n)
{
    if (n->is_zero())
        return integer(1);

    map_integer_uint prime_mul;
    integer_class lambda, t, p;
    unsigned multiplicity;

    prime_factor_multiplicities(prime_mul, *n);
    lambda = 1;
    for (const auto &it : prime_mul) {
        p = it.first->as_integer_class();
        multiplicity = it.second;
        if (p == 2
            and multiplicity
                    > 2) { // For powers of 2 greater than 4 divide by 2.
            multiplicity--;
        }
        t = p - 1;
        mp_lcm(lambda, lambda, t);
        mp_pow_ui(t, p, multiplicity - 1);
        // lambda and p are relatively prime.
        lambda = lambda * t;
    }
    return integer(std::move(lambda));
}

// References : Cohen H., A course in computational algebraic number theory
// (1996), page 25.
bool multiplicative_order(const Ptr<RCP<const Integer>> &o,
                          const RCP<const Integer> &a,
                          const RCP<const Integer> &n)
{
    integer_class order, p, t;
    integer_class _a = a->as_integer_class(),
                  _n = mp_abs(n->as_integer_class());
    mp_gcd(t, _a, _n);
    if (t != 1)
        return false;

    RCP<const Integer> lambda = carmichael(n);
    map_integer_uint prime_mul;
    prime_factor_multiplicities(prime_mul, *lambda);
    _a %= _n;
    order = lambda->as_integer_class();

    for (const auto &it : prime_mul) {
        p = it.first->as_integer_class();
        mp_pow_ui(t, p, it.second);
        mp_divexact(order, order, t);
        mp_powm(t, _a, order, _n);
        while (t != 1) {
            mp_powm(t, t, p, _n);
            order *= p;
        }
    }
    *o = integer(std::move(order));
    return true;
}
int legendre(const Integer &a, const Integer &n)
{
    return mp_legendre(a.as_integer_class(), n.as_integer_class());
}

int jacobi(const Integer &a, const Integer &n)
{
    return mp_jacobi(a.as_integer_class(), n.as_integer_class());
}

int kronecker(const Integer &a, const Integer &n)
{
    return mp_kronecker(a.as_integer_class(), n.as_integer_class());
}

namespace
{
bool _sqrt_mod_tonelli_shanks(integer_class &rop, const integer_class &a,
                              const integer_class &p)
{
    mp_randstate state;
    integer_class n, y, b, q, pm1, t(1);
    pm1 = p - 1;
    unsigned e, m;
    e = numeric_cast<unsigned>(mp_scan1(pm1));
    q = pm1 >> e; // p - 1 = 2**e*q

    while (t != -1) {
        state.urandomint(n, p);
        t = mp_legendre(n, p);
    }
    mp_powm(y, n, q, p); // y = n**q mod p
    mp_powm(b, a, q, p); // b = a**q mod p
    t = (q + 1) / 2;
    mp_powm(rop, a, t, p); // rop = a**((q + 1) / 2) mod p

    while (b != 1) {
        m = 0;
        t = b;
        while (t != 1) {
            mp_powm(t, t, integer_class(2), p);
            ++m; // t = t**2 = b**2**(m)
        }
        if (m == e)
            return false;
        mp_pow_ui(q, integer_class(2), e - m - 1); // q = 2**(e - m - 1)
        mp_powm(t, y, q, p);                       // t = y**(2**(e - m - 1))
        mp_powm(y, t, integer_class(2), p);        // y = t**2
        e = m;
        rop = (rop * t) % p;
        b = (b * y) % p;
    }
    return true;
}

bool _sqrt_mod_prime(integer_class &rop, const integer_class &a,
                     const integer_class &p)
{
    if (p == 2) {
        rop = a % p;
        return true;
    }
    int l = mp_legendre(a, p);
    integer_class t;
    if (l == -1) {
        return false;
    } else if (l == 0) {
        rop = 0;
    } else if (p % 4 == 3) {
        t = (p + 1) / 4;
        mp_powm(rop, a, t, p);
    } else if (p % 8 == 5) {
        t = (p - 1) / 4;
        mp_powm(t, a, t, p);
        if (t == 1) {
            t = (p + 3) / 8;
            mp_powm(rop, a, t, p);
        } else {
            t = (p - 5) / 8;
            integer_class t1 = 4 * a;
            mp_powm(t, t1, t, p);
            rop = (2 * a * t) % p;
        }
    } else {
        if (p < 10000) { // If p < 10000, brute force is faster.
            integer_class sq = integer_class(1), _a;
            mp_fdiv_r(_a, a, p);
            for (unsigned i = 1; i < p; ++i) {
                if (sq == _a) {
                    rop = i;
                    return true;
                }
                sq += 2 * i + 1;
                mp_fdiv_r(sq, sq, p);
            }
            return false;
        } else {
            return _sqrt_mod_tonelli_shanks(rop, a, p);
        }
    }
    return true;
}

// References : Menezes, Alfred J., Paul C. Van Oorschot, and Scott A. Vanstone.
// Handbook of applied cryptography. CRC press, 2010. pages 104 - 108
// Calculates log = x mod q**k where g**x == a mod p and order(g, p) = n.
void _discrete_log(integer_class &log, const integer_class &a,
                   const integer_class &g, const integer_class &n,
                   const integer_class &q, const unsigned &k,
                   const integer_class &p)
{
    log = 0;
    integer_class gamma = a, alpha, _n, t, beta, qj(1), m, l;
    _n = n / q;
    mp_powm(alpha, g, _n, p);
    mp_sqrtrem(m, t, q);
    if (t != 0)
        ++m; // m = ceiling(sqrt(q)).
    map_integer_uint
        table; // Table for lookup in baby-step giant-step algorithm
    integer_class alpha_j(1), d, s;
    s = -m;
    mp_powm(s, alpha, s, p);

    for (unsigned j = 0; j < m; ++j) {
        insert(table, integer(alpha_j), j);
        alpha_j = (alpha_j * alpha) % p;
    }

    for (unsigned long j = 0; j < k; ++j) { // Pohlig-Hellman
        mp_powm(beta, gamma, _n, p);
        // Baby-step giant-step algorithm for l = log_alpha(beta)
        d = beta;
        bool found = false;
        for (unsigned i = 0; not found && i < m; ++i) {
            if (table.find(integer(d)) != table.end()) {
                l = i * m + table[integer(d)];
                found = true;
                break;
            }
            d = (d * s) % p;
        }
        _n /= q;
        t = -l * qj;

        log -= t;
        mp_powm(t, g, t, p);
        gamma *= t; // gamma *= g ** (-l * (q ** j))
        qj *= q;
    }
}

// References : Johnston A., A generalised qth root algorithm.
// Solution for x**n == a mod p**k where a != 0 mod p and p is an odd prime.
bool _nthroot_mod1(std::vector<RCP<const Integer>> &roots,
                   const integer_class &a, const integer_class &n,
                   const integer_class &p, const unsigned k,
                   bool all_roots = false)
{
    integer_class _n, r, root, s, t, g(0), pk, m, phi;
    mp_pow_ui(pk, p, k);
    phi = pk * (p - 1) / p;
    mp_gcd(m, phi, n);
    t = phi / m;
    mp_powm(t, a, t, pk);
    // Check whether a**(phi / gcd(phi, n)) == 1 mod p**k.
    if (t != 1) {
        return false;
    }
    // Solve x**n == a mod p first.
    t = p - 1;
    mp_gcdext(_n, r, s, n, t);
    if (r < 0) {
        mp_fdiv_r(r, r, t / _n);
    }
    mp_powm(s, a, r, p);

    // Solve x**(_n) == s mod p where _n | p - 1.
    if (_n == 1) {
        root = s;
    } else if (_n == 2) {
        _sqrt_mod_prime(root, s, p);
    } else { // Ref[1]
        map_integer_uint prime_mul;
        prime_factor_multiplicities(prime_mul, *integer(_n));
        integer_class h, q, qt, z, v, x, s1 = s;
        _primitive_root(g, p, integer_class(2));
        unsigned c;
        for (const auto &it : prime_mul) {
            q = it.first->as_integer_class();
            mp_pow_ui(qt, q, it.second);
            h = (p - 1) / q;
            c = 1;
            while (h % q == 0) {
                ++c;
                h /= q;
            }
            mp_invert(t, h, qt);
            z = t * -h;
            x = (1 + z) / qt;
            mp_powm(v, s1, x, p);

            if (c == it.second) {
                s1 = v;
            } else {
                mp_powm(x, s1, h, p);
                t = h * qt;
                mp_powm(r, g, t, p);
                mp_pow_ui(qt, q, c - it.second);
                _discrete_log(t, x, r, qt, q, c - it.second, p);
                t = -z * t;
                mp_powm(r, g, t, p);
                v *= r;
                mp_fdiv_r(v, v, p);
                s1 = v;
            }
        }
        root = s1;
    }
    r = n;
    unsigned c = 0;
    while (r % p == 0) {
        mp_divexact(r, r, p);
        ++c;
    }

    // Solve s == x**r mod p**k where (x**r)**(p**c)) == a mod p**k
    integer_class pc = n / r, pd = pc * p;
    if (c >= 1) {
        mp_powm(s, root, r, p);
        // s == root**r mod p. Since s**(p**c) == 1 == a mod p**(c + 1), lift
        // until p**k.
        for (unsigned d = c + 2; d <= k; ++d) {
            t = 1 - pc;
            pd *= p;
            mp_powm(t, s, t, pd);
            t = (a * t - s) / pc;
            s += t;
        }
    } else {
        s = a;
    }

    // Solve x**r == s mod p**k given that root**r == s mod p and r % p != 0.
    integer_class u;
    pd = p;
    for (unsigned d = 2; d < 2 * k; d *= 2) { // Hensel lifting
        t = r - 1;
        pd *= pd;
        if (d > k)
            pd = pk;
        mp_powm(u, root, t, pd);
        t = r * u;
        mp_invert(t, t, pd);
        root += (s - u * root) * t;
        mp_fdiv_r(root, root, pd);
    }
    if (m != 1 and all_roots) {
        // All roots are generated by root*(g**(phi / gcd(phi , n)))**j
        if (n == 2) {
            t = -1;
        } else {
            if (g == 0)
                _primitive_root(g, p, integer_class(2));
            t = phi / m;
            mp_powm(t, g, t, pk);
        }
        for (unsigned j = 0; j < m; ++j) {
            roots.push_back(integer(root));
            root *= t;
            mp_fdiv_r(root, root, pk);
        }
    } else {
        roots.push_back(integer(root));
    }
    return true;
}

// Checks if Solution for x**n == a mod p**k exists where a != 0 mod p and p is
// an odd prime.
bool _is_nthroot_mod1(const integer_class &a, const integer_class &n,
                      const integer_class &p, const unsigned k)
{
    integer_class t, pk, m, phi;
    mp_pow_ui(pk, p, k);
    phi = pk * (p - 1) / p;
    mp_gcd(m, phi, n);
    t = phi / m;
    mp_powm(t, a, t, pk);
    // Check whether a**(phi / gcd(phi, n)) == 1 mod p**k.
    if (t != 1) {
        return false;
    }
    return true;
}

// Solution for x**n == a mod p**k.
bool _nthroot_mod_prime_power(std::vector<RCP<const Integer>> &roots,
                              const integer_class &a, const integer_class &n,
                              const integer_class &p, const unsigned k,
                              bool all_roots = false)
{
    integer_class pk, root;
    std::vector<RCP<const Integer>> _roots;
    if (a % p != 0) {
        if (p == 2) {
            integer_class r = n, t, s, pc, pj;
            pk = integer_class(1) << k;
            unsigned c = numeric_cast<unsigned>(mp_scan1(n));
            r = n >> c; // n = 2**c * r where r is odd.

            // Handle special cases of k = 1 and k = 2.
            if (k == 1) {
                roots.push_back(integer(1));
                return true;
            }
            if (k == 2) {
                if (c > 0 and a % 4 == 3) {
                    return false;
                }
                roots.push_back(integer(a % 4));
                if (all_roots and c > 0)
                    roots.push_back(integer(3));
                return true;
            }
            if (c >= k - 2) {
                c = k - 2; // Since x**(2**c) == x**(2**(k - 2)) mod 2**k, let c
                           // = k - 2.
            }
            t = integer_class(1) << (k - 2);
            pc = integer_class(1) << c;

            mp_invert(s, r, t);
            if (c == 0) {
                // x**r == a mod 2**k and x**2**(k - 2) == 1 mod 2**k, implies
                // x**(r * s) == x == a**s mod 2**k.
                mp_powm(root, a, s, pk);
                roots.push_back(integer(root));
                return true;
            }

            // First, solve for y**2**c == a mod 2**k where y == x**r
            t = integer_class(1) << (c + 2);
            mp_fdiv_r(t, a, t);
            // Check for a == y**2**c == 1 mod 2**(c + 2).
            if (t != 1)
                return false;
            root = 1;
            pj = pc * 4;
            // 1 is a root of x**2**c == 1 mod 2**(c + 2). Lift till 2**k.
            for (unsigned j = c + 2; j < k; ++j) {
                pj *= 2;
                mp_powm(t, root, pc, pj);
                t -= a;
                if (t % pj != 0)
                    // Add 2**(j - c).
                    root += integer_class(1) << (j - c);
            }
            // Solve x**r == root mod 2**k.
            mp_powm(root, root, s, pk);

            if (all_roots) {
                // All roots are generated by, root * (j * (2**(k - c) +/- 1)).
                t = pk / pc * root;
                for (unsigned i = 0; i < 2; ++i) {
                    for (unsigned long j = 0; j < pc; ++j) {
                        roots.push_back(integer(root));
                        root += t;
                    }
                    root = t - root;
                }
            } else {
                roots.push_back(integer(root));
            }
            return true;
        } else {
            return _nthroot_mod1(roots, a, n, p, k, all_roots);
        }
    } else {
        integer_class _a;
        mp_pow_ui(pk, p, k);
        _a = a % pk;
        unsigned m;
        integer_class pm;
        if (_a == 0) {
            if (not all_roots) {
                roots.push_back(integer(0));
                return true;
            }
            _roots.push_back(integer(0));
            if (n >= k)
                m = k - 1;
            else
                m = numeric_cast<unsigned>(k - 1 - (k - 1) / mp_get_ui(n));
            mp_pow_ui(pm, p, m);
        } else {
            unsigned r = 1;
            mp_divexact(_a, _a, p);
            while (_a % p == 0) {
                mp_divexact(_a, _a, p);
                ++r;
            }
            if (r < n or r % n != 0
                or not _nthroot_mod_prime_power(_roots, _a, n, p, k - r,
                                                all_roots)) {
                return false;
            }
            m = numeric_cast<unsigned>(r / mp_get_ui(n));
            mp_pow_ui(pm, p, m);
            if (not all_roots) {
                roots.push_back(
                    integer(_roots.back()->as_integer_class() * pm));
                return true;
            }
            for (auto &it : _roots) {
                it = integer(it->as_integer_class() * pm);
            }
            m = numeric_cast<unsigned>(r - r / mp_get_ui(n));
            mp_pow_ui(pm, p, m);
        }
        integer_class pkm;
        mp_pow_ui(pkm, p, k - m);

        for (const auto &it : _roots) {
            root = it->as_integer_class();
            for (unsigned long i = 0; i < pm; ++i) {
                roots.push_back(integer(root));
                root += pkm;
            }
        }
    }
    return true;
}
} // anonymous namespace

// Returns whether Solution for x**n == a mod p**k exists or not
bool _is_nthroot_mod_prime_power(const integer_class &a, const integer_class &n,
                                 const integer_class &p, const unsigned k)
{
    integer_class pk;
    if (a % p != 0) {
        if (p == 2) {
            integer_class t;
            unsigned c = numeric_cast<unsigned>(mp_scan1(n));

            // Handle special cases of k = 1 and k = 2.
            if (k == 1) {
                return true;
            }
            if (k == 2) {
                if (c > 0 and a % 4 == 3) {
                    return false;
                }
                return true;
            }
            if (c >= k - 2) {
                c = k - 2; // Since x**(2**c) == x**(2**(k - 2)) mod 2**k, let c
                           // = k - 2.
            }
            if (c == 0) {
                // x**r == a mod 2**k and x**2**(k - 2) == 1 mod 2**k, implies
                // x**(r * s) == x == a**s mod 2**k.
                return true;
            }

            // First, solve for y**2**c == a mod 2**k where y == x**r
            t = integer_class(1) << (c + 2);
            mp_fdiv_r(t, a, t);
            // Check for a == y**2**c == 1 mod 2**(c + 2).
            if (t != 1)
                return false;
            return true;
        } else {
            return _is_nthroot_mod1(a, n, p, k);
        }
    } else {
        integer_class _a;
        mp_pow_ui(pk, p, k);
        _a = a % pk;
        integer_class pm;
        if (_a == 0) {
            return true;
        } else {
            unsigned r = 1;
            mp_divexact(_a, _a, p);
            while (_a % p == 0) {
                mp_divexact(_a, _a, p);
                ++r;
            }
            if (r < n or r % n != 0
                or not _is_nthroot_mod_prime_power(_a, n, p, k - r)) {
                return false;
            }
            return true;
        }
    }
    return true;
}

bool nthroot_mod(const Ptr<RCP<const Integer>> &root,
                 const RCP<const Integer> &a, const RCP<const Integer> &n,
                 const RCP<const Integer> &mod)
{
    if (mod->as_integer_class() <= 0) {
        return false;
    } else if (mod->as_integer_class() == 1) {
        *root = integer(0);
        return true;
    }
    map_integer_uint prime_mul;
    prime_factor_multiplicities(prime_mul, *mod);
    std::vector<RCP<const Integer>> moduli;
    bool ret_val;

    std::vector<RCP<const Integer>> rem;
    for (const auto &it : prime_mul) {
        integer_class _mod;
        mp_pow_ui(_mod, it.first->as_integer_class(), it.second);
        moduli.push_back(integer(std::move(_mod)));
        ret_val = _nthroot_mod_prime_power(
            rem, a->as_integer_class(), n->as_integer_class(),
            it.first->as_integer_class(), it.second, false);
        if (not ret_val)
            return false;
    }
    crt(root, rem, moduli);
    return true;
}

void nthroot_mod_list(std::vector<RCP<const Integer>> &roots,
                      const RCP<const Integer> &a, const RCP<const Integer> &n,
                      const RCP<const Integer> &m)
{
    if (m->as_integer_class() <= 0) {
        return;
    } else if (m->as_integer_class() == 1) {
        roots.push_back(integer(0));
        return;
    }
    map_integer_uint prime_mul;
    prime_factor_multiplicities(prime_mul, *m);
    std::vector<RCP<const Integer>> moduli;
    bool ret_val;

    std::vector<std::vector<RCP<const Integer>>> rem;
    for (const auto &it : prime_mul) {
        integer_class _mod;
        mp_pow_ui(_mod, it.first->as_integer_class(), it.second);
        moduli.push_back(integer(std::move(_mod)));
        std::vector<RCP<const Integer>> rem1;
        ret_val = _nthroot_mod_prime_power(
            rem1, a->as_integer_class(), n->as_integer_class(),
            it.first->as_integer_class(), it.second, true);
        if (not ret_val)
            return;
        rem.push_back(rem1);
    }
    _crt_cartesian(roots, rem, moduli);
    std::sort(roots.begin(), roots.end(), SymEngine::RCPIntegerKeyLess());
}

bool powermod(const Ptr<RCP<const Integer>> &powm, const RCP<const Integer> &a,
              const RCP<const Number> &b, const RCP<const Integer> &m)
{
    if (is_a<Integer>(*b)) {
        integer_class t = down_cast<const Integer &>(*b).as_integer_class();
        if (b->is_negative())
            t *= -1;
        mp_powm(t, a->as_integer_class(), t, m->as_integer_class());
        if (b->is_negative()) {
            bool ret_val = mp_invert(t, t, m->as_integer_class());
            if (not ret_val)
                return false;
        }
        *powm = integer(std::move(t));
        return true;
    } else if (is_a<Rational>(*b)) {
        RCP<const Integer> num, den, r;
        get_num_den(down_cast<const Rational &>(*b), outArg(num), outArg(den));
        if (den->is_negative()) {
            den = den->mulint(*minus_one);
            num = num->mulint(*minus_one);
        }
        integer_class t = mp_abs(num->as_integer_class());
        mp_powm(t, a->as_integer_class(), t, m->as_integer_class());
        if (num->is_negative()) {
            bool ret_val = mp_invert(t, t, m->as_integer_class());
            if (not ret_val)
                return false;
        }
        r = integer(std::move(t));
        return nthroot_mod(powm, r, den, m);
    }
    return false;
}

void powermod_list(std::vector<RCP<const Integer>> &pows,
                   const RCP<const Integer> &a, const RCP<const Number> &b,
                   const RCP<const Integer> &m)
{
    if (is_a<Integer>(*b)) {
        integer_class t
            = mp_abs(down_cast<const Integer &>(*b).as_integer_class());
        mp_powm(t, a->as_integer_class(), t, m->as_integer_class());
        if (b->is_negative()) {
            bool ret_val = mp_invert(t, t, m->as_integer_class());
            if (not ret_val)
                return;
        }
        pows.push_back(integer(std::move(t)));
    } else if (is_a<Rational>(*b)) {
        RCP<const Integer> num, den, r;
        get_num_den(down_cast<const Rational &>(*b), outArg(num), outArg(den));
        if (den->is_negative()) {
            den = den->mulint(*integer(-1));
            num = num->mulint(*integer(-1));
        }
        integer_class t = num->as_integer_class();
        if (num->is_negative())
            t *= -1;
        mp_powm(t, a->as_integer_class(), t, m->as_integer_class());
        if (num->is_negative()) {
            bool ret_val = mp_invert(t, t, m->as_integer_class());
            if (not ret_val)
                return;
        }
        r = integer(t);
        nthroot_mod_list(pows, r, den, m);
    }
}

vec_integer_class quadratic_residues(const Integer &a)
{
    /*
        Returns the list of quadratic residues.
        Example
        ========
        >>> quadratic_residues(7)
        [0, 1, 2, 4]
    */

    if (a.as_integer_class() < 1) {
        throw SymEngineException("quadratic_residues: Input must be > 0");
    }

    vec_integer_class residue;
    for (integer_class i = integer_class(0); i <= a.as_int() / 2; i++) {
        residue.push_back((i * i) % a.as_int());
    }

    sort(residue.begin(), residue.end());
    residue.erase(unique(residue.begin(), residue.end()), residue.end());

    return residue;
}

bool is_quad_residue(const Integer &a, const Integer &p)
{
    /*
    Returns true if ``a`` (mod ``p``) is in the set of squares mod ``p``,
    i.e a % p in set([i**2 % p for i in range(p)]). If ``p`` is an odd but
    not prime, an iterative method is used to make the determination.
    */

    integer_class p2 = p.as_integer_class();
    if (p2 == 0)
        throw SymEngineException(
            "is_quad_residue: Second parameter must be non-zero");
    if (p2 < 0)
        p2 = -p2;
    integer_class a_final = a.as_integer_class();
    if (a.as_integer_class() >= p2 || a.as_integer_class() < 0)
        mp_fdiv_r(a_final, a.as_integer_class(), p2);
    if (a_final < 2)
        return true;

    if (!probab_prime_p(*integer(p2))) {
        if ((p2 % 2 == 1) && jacobi(*integer(a_final), p) == -1)
            return false;

        const RCP<const Integer> a1 = integer(a_final);
        const RCP<const Integer> p1 = integer(p2);

        map_integer_uint prime_mul;
        prime_factor_multiplicities(prime_mul, *p1);
        bool ret_val;

        for (const auto &it : prime_mul) {
            ret_val = _is_nthroot_mod_prime_power(
                a1->as_integer_class(), integer(2)->as_integer_class(),
                it.first->as_integer_class(), it.second);
            if (not ret_val)
                return false;
        }
        return true;
    }

    return mp_legendre(a_final, p2) == 1;
}

bool is_nth_residue(const Integer &a, const Integer &n, const Integer &mod)
/*
Returns true if ``a`` (mod ``mod``) is in the set of nth powers mod ``mod``,
i.e a % mod in set([i**n % mod for i in range(mod)]).
*/
{
    integer_class _mod = mod.as_integer_class();

    if (_mod == 0) {
        return false;
    } else if (_mod == 1) {
        return true;
    }

    if (_mod < 0)
        _mod = -(_mod);

    RCP<const Integer> mod2 = integer(_mod);
    map_integer_uint prime_mul;
    prime_factor_multiplicities(prime_mul, *mod2);
    bool ret_val;

    for (const auto &it : prime_mul) {
        ret_val = _is_nthroot_mod_prime_power(
            a.as_integer_class(), n.as_integer_class(),
            it.first->as_integer_class(), it.second);
        if (not ret_val)
            return false;
    }
    return true;
}

int mobius(const Integer &a)
{
    if (a.as_int() <= 0) {
        throw SymEngineException("mobius: Integer <= 0");
    }
    map_integer_uint prime_mul;
    bool is_square_free = true;
    prime_factor_multiplicities(prime_mul, a);
    auto num_prime_factors = prime_mul.size();
    for (const auto &it : prime_mul) {
        int p_freq = it.second;
        if (p_freq > 1) {
            is_square_free = false;
            break;
        }
    }
    if (!is_square_free) {
        return 0;
    } else if (num_prime_factors % 2 == 0) {
        return 1;
    } else {
        return -1;
    }
}

long mertens(const unsigned long a)
{
    long mertens = 0;
    for (unsigned long i = 1; i <= a; ++i) {
        mertens += mobius(*(integer(i)));
    }
    return mertens;
}

/**
 * @brief Numeric calculation of the n:th s-gonal number
 * @param s Number of sides of the polygon. Must be greater than 2.
 * @param n Must be greater than 0
 * @returns The n:th s-gonal number
 *
 * A fast pure numeric calculation of the n:th s-gonal number. No bounds
 * checking of the input is performed.
 * See https://en.wikipedia.org/wiki/Polygonal_number for source of formula.
 */
integer_class mp_polygonal_number(const integer_class &s,
                                  const integer_class &n)
{
    auto res = ((s - 2) * n * n - (s - 4) * n) / 2;
    return res;
}

/**
 * @brief Numeric calculation of the principal s-gonal root of x
 * @param s Number of sides of the polygon. Must be greater than 2.
 * @param x An integer greater than 0
 * @returns The root
 *
 * A fast pure numeric calculation of the principal (i.e. positive) s-gonal root
 * of x. No bounds checking of the input is performed.
 * See https://en.wikipedia.org/wiki/Polygonal_number for source of formula.
 */
integer_class mp_principal_polygonal_root(const integer_class &s,
                                          const integer_class &x)
{
    integer_class tmp;
    mp_pow_ui(tmp, s - 4, 2);
    integer_class root = mp_sqrt(8 * x * (s - 2) + tmp);
    integer_class n = (root + s - 4) / (2 * (s - 2));
    return n;
}

std::pair<integer_class, integer_class>
mp_perfect_power_decomposition(const integer_class &n, bool lowest_exponent)
{
    // From
    // https://codegolf.stackexchange.com/questions/1935/fastest-algorithm-for-decomposing-a-perfect-power
    unsigned long p = 2;
    integer_class intone, i, j, m, res;
    intone = 1;
    std::pair<integer_class, integer_class> respair;
    respair = std::make_pair(n, intone);

    while ((intone << p) <= n) {
        i = 2;
        j = n;
        while (j > i + 1) {
            m = (i + j) / 2;
            mp_pow_ui(res, m, p);
            if (res > n)
                j = m;
            else
                i = m;
        }
        mp_pow_ui(res, i, p);
        if (res == n) {
            respair = std::make_pair(i, p);
            if (lowest_exponent) {
                return respair;
            }
        }
        p++;
    }
    return respair;
}

} // namespace SymEngine


// Deal.ii
#include <deal.II/base/tensor_function.h>
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_out.h>

// STL
#include <iostream>
#include <fstream>
#include <cmath>

//#include "reconstruction_mesh.hpp"

#include <deal.II/base/logstream.h>

using namespace dealii;

template <int dim> class AdvectionField : public TensorFunction<1, dim> {
public:
  AdvectionField() : TensorFunction<1, dim>() {}

  virtual Tensor<1, dim> value(const Point<dim> &point) const override;
  virtual void value_list(const std::vector<Point<dim>> &points,
                          std::vector<Tensor<1, dim>> &values) const override;

private:
  const double pi = numbers::PI;
};

template <int dim>
Tensor<1, dim> AdvectionField<dim>::value(const Point<dim> &p) const
{
  const double t = this->get_time();

  Tensor<1, dim> value;
  value.clear();

  double t0 = 0;

    const double tn = 1;

    const double n=10;

    const double dt = (tn-t0)/n;

    const double c=3;

  // Here velocity is consider to be constant let it be 1 m/s.

  for (unsigned int d = 0; d < dim; ++d)

    {

      value[d] =0.8 * std::sin( 3.14* p[1]);

     // value[d] =1; //0.8 * std::sin( numbers::PI * p[1]);

    }

  return value;
}

template <int dim>
void AdvectionField<dim>::value_list(
    const std::vector<Point<dim>> &points,
    std::vector<Tensor<1, dim>> &values) const {
  Assert(points.size() == values.size(),
         ExcDimensionMismatch(points.size(), values.size()));

  for (unsigned int p = 0; p < points.size(); ++p) {
    values[p].clear();
    values[p] = value(points[p]);
  }
}

///////////////////////////////////////////////////////////////

template <int dim> class RK4 : public Tensor<1, dim> {
public:
  RK4() : Tensor<1, dim>() {}
  virtual Tensor<1, dim> value(const Point<dim> &p) const;

private:
  AdvectionField<dim> advection_field;
};

template <int dim> Tensor<1, dim> RK4<dim>::value(const Point<dim> &p) const {

  Tensor<1, dim> yn;

  double t0 = 0;

  const double tn = 1;

  const double n=10;

  const double dt = (tn-t0)/n;

  /* Here the equation is dy/dt =c; where c is velocity y is the vertex of mesh and t is the time
   *
   */

  yn = advection_field.value(p);

  for (double t0 = 0; t0 <= tn; t0++) {
    auto k1 = dt * advection_field.value(p),
         k2 = dt * (advection_field.value(p) + k1 / 2),
         k3 = dt * (advection_field.value(p) + k2 / 2),
         k4 = dt * (advection_field.value(p) + k3);
    return yn += (1 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4);
    t0 = t0 + dt;
  }
}

///////////////////////////////////////////////////////////////////////////////////////////////

template <int dim> class MeshDeformer {
public:
  MeshDeformer();

  void run();

private:
  void make_grid();

  Triangulation<dim> triangulation;

  GridOut grid_out;
  AdvectionField<dim> advection_field;
  Vector<double> solution;
  RK4<dim> rk4;

  double time;
  double time_step;
  /*!
   * Final simulation time.
   */
  double T_max;
};

template <int dim>
MeshDeformer<dim>::MeshDeformer()
    : triangulation(), time(1.0), time_step(1), T_max(1.0) {}

template <int dim> void MeshDeformer<dim>::make_grid()
{
  GridGenerator::hyper_cube(triangulation, 0, 1, /* colorize faces */
                            false);
  triangulation.refine_global(4);

  std::vector<Tensor<1, dim>> vetices_on_cell;
   		 while (T_max>=0.09) {
  for (const auto &cell : triangulation.active_cell_iterators()) {

    for (unsigned int i = 0; i < GeometryInfo<dim>::vertices_per_cell; i++) {
      Point<2> &v = cell->vertex(i); // initial vertex

      rk4.value(v);

      std::cout << "----------------------------------" << std::endl;

      std::cout << rk4.value(v) << "  rk4    " << std::endl;
      std::cout << "----------------------------------" << std::endl;

      GridOut       grid_out;
       											std::ofstream output("mesh-" + std::to_string(T_max) + ".vtu");
       											grid_out.write_vtu( triangulation, output);
       										 T_max=T_max-time_step;
    }
  }
  }

}

template <int dim> void MeshDeformer<dim>::run()
{
	make_grid();
}

int main()
{

  MeshDeformer<2> rk4_problem_2d;
  rk4_problem_2d.run();

  return 0;
}


/*****************************************************************************
*
* Rokko: Integrated Interface for libraries of eigenvalue decomposition
*
* Copyright (C) 2014 by Synge Todo <wistaria@comp-phys.org>
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*
*****************************************************************************/

// C++ version of TITPACK Ver.2 by H. Nishimori

/************ Sample main program #11 *****************
* 1d Heisenberg antiferromagnet with 8 spins
* Eigenvalues and an eigenvector by diag
******************************************************/

#include <mpi.h>
#include <iostream>
#include <rokko/rokko.hpp>
#include <boost/timer.hpp>
#include "titpack.hpp"
#include "options.hpp"

typedef rokko::parallel_dense_solver solver_type;
typedef rokko::distributed_matrix<rokko::matrix_col_major> matrix_type;

int main(int argc, char** argv) {
  int provided;
  MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
  rokko::grid g;

  std::cout.precision(10);
  options opt(argc, argv, 8, solver_type::default_solver(), g.get_myrank() == 0);
  if (!opt.valid) MPI_Abort(MPI_COMM_WORLD, 1);
  boost::timer tm;
  MPI_Barrier(g.get_comm());
  double t1 = tm.elapsed();

  // lattice structure
  int n = opt.N;
  int ibond = n;
  std::vector<int> ipair;
  for (int i = 0; i < ibond; ++i) {
    ipair.push_back(i);
    ipair.push_back((i + 1) % n);
  }

  // Hamiltonian parameters
  std::vector<double> bondwt(ibond, -1);
  std::vector<double> zrtio(ibond, 1);

  // table of configurations and Hamiltonian operator
  subspace ss(n, 0);
  hamiltonian hop(ss, ipair, bondwt, zrtio);
  solver_type solver(opt.solver);
  solver.initialize(argc, argv);

  // Hamiltonian matrix
  matrix_type elemnt(hop.dimension(), hop.dimension(), g, solver);
  elm3(hop, elemnt);
  MPI_Barrier(g.get_comm());
  double t2 = tm.elapsed();

  rokko::localized_vector E(hop.dimension());
  matrix_type v(hop.dimension(), hop.dimension(), g, solver);
  solver.diagonalize(elemnt, E, v);
  double t3 = tm.elapsed();

  if (g.get_myrank() == 0) {
    int ne = 4;
    std::cout << "[Eigenvalues]\n";
    for (int i = 0; i < ne; ++i) std::cout << '\t' << E[i];
    std::cout << std::endl << std::flush;
  }
  MPI_Barrier(g.get_comm());

  // Do not forget to call elm3 again before calling check3
  elm3(hop, elemnt);
  matrix_type w(hop.dimension(), 1, g, solver);
  check3_mpi(elemnt, v, 0, w);
  std::cout << std::flush;
  MPI_Barrier(g.get_comm());
  double t4 = tm.elapsed();

  std::vector<int> npair;
  npair.push_back(1);
  npair.push_back(2);
  std::vector<double> sxx(1);
  matrix_type sxmat(hop.dimension(), hop.dimension(), g, solver);
  xcorr3_mpi(ss, npair, v, 0, sxx, sxmat, w);
  if (v.is_gindex(0, 0)) std::cout << "sxx: " << sxx[0] << std::endl;
  std::cout << std::flush;
  MPI_Barrier(g.get_comm());
  std::vector<double> szz(1);
  zcorr_mpi(ss, npair, v, 0, szz);
  if (g.get_myrank() == 0) std::cout << "szz: " << szz[0] << std::endl;
  std::cout << std::flush;
  MPI_Barrier(g.get_comm());
  double t5 = tm.elapsed();

  if (g.get_myrank() == 0) {
    std::cerr << "initialize      " << (t2-t1) << " sec\n"
              << "diagonalization " << (t3-t2) << " sec\n"
              << "check           " << (t4-t3) << " sec\n"
              << "correlation     " << (t5-t4) << " sec\n";
  }
  MPI_Finalize();
}


#ifndef BIGGLES_PARTITION_SAMPLER_HPP__
#define BIGGLES_PARTITION_SAMPLER_HPP__

#include <boost/tuple/tuple.hpp>

#include "model.hpp"
#include "partition.hpp"
#include "mh_moves/mh_moves.hpp"
#include "sampling/metropolis_hastings.hpp"
#include "sampling/simple.hpp"

namespace biggles {

/// @brief A class which can sample a set of tracks and clutter given a set of model parameters.
///
/// Internally this class is implemented via the Metropolis-Hastings algorithm. See the documentation for sample() for
/// more information.
///
/// This samples from the posterior distribution \f$ P(T | \theta, D) \f$ where \f$ T \f$ is a set of tracks and
/// clutter which encompass all observations, \f$ \theta \f$ is a set of model parameters and \f$ D \f$ are the
/// observations.
///
/// We can re-arrange this posterior using the law of conditional probability which states \f$ P(T | \theta, D) =
/// P(T, \theta | D) / P(\theta | D) \f$ combined with Bayes' theorem:
///
/// \f[
/// P(T, \theta | D)
///     = \frac{P(D|T, \theta) P(T, \theta)}{P(D)}
///     = \frac{P(D|T, \theta) P(T | \theta) P(\theta)}{P(D)}
/// \f]
///
/// and hence
///
/// \f[
/// P(T | \theta, D)
///     = \frac{P(D|T, \theta) P(T | \theta) P(\theta)}{P(\theta | D)P(D)}
///     = \frac{P(D|T, \theta) P(T | \theta) P(\theta)}{P(\theta, D)}.
/// \f]
///
/// Internally we use the Metropolis-Hastings algorithm which requires only a value proportional to this density.
/// Removing terms independent of \f$ T \f$ we obtain:
///
/// \f[
/// P(T | \theta, D) \propto P(D|T, \theta) P(T | \theta) P(\theta).
/// \f]
///
/// The \f$ P(\theta) \f$ term is simply the prior on the model parameters. The \f$ P(T | \theta) \f$ term is a
/// little more subtle; it is the likelihood of the tracks independent of the observations within them. This is
/// a function of the model parameters and birth and death times for the tracks only. Since the data is partitioned
/// between tracks and the clutter with no overlap, we may factorise the data likelihood term as follows:
///
/// \f[
/// P(D | T, \theta) = P(d_0 | \theta) \prod_{i = 1}^K P(d_i | t_i, \theta)
/// \f]
///
/// where \f$ d_0 \f$ is used to represent the clutter observations and \f$ d_1, ..., d_K \f$ are the \f$ K \f$
/// sets of observations corresponding to the \f$ K \f$ tracks in the partition. \f$ P(d_0 | \theta) \f$ is the
/// likelihood of having seen the clutter observations we have given the parameters. \f$ P(d_i | t_i, \theta) \f$ is
/// the likelihood of track \f$ i \f$ having generated the data we saw.
///
/// Taking logarithms, we obtain the final log density function used within the Metropolis-Hastings sampler:
///
/// \f[
/// \ell(T | \theta, D) = \kappa + \ell(d_0 | t_0, \theta) + \sum_{i = 1}^K \ell(d_i | t_i, \theta) + \ell(T | \theta) + \ell(\theta)
/// \f]
///
/// where \f$ \ell(\cdot) \f$ is used to denote the log-PDF and \f$ \kappa \f$ is some arbitrary normalising offset.
/// Without loss of generality, we set it to zero.
///
/// Each log-PDF term in the expansion above is calculated by one of the log-PDF functions in biggles/model.hpp.
class partition_sampler : public sampling::metropolis_hastings_sampler
{
private:
    typedef sampling::metropolis_hastings_sampler base_type;
public:
    /// @brief Construct a sampler from an initial partition and set of model parameters.
    ///
    /// @param initial_partition
    /// @param initial_parameters
    partition_sampler(const partition_ptr_t& initial_partition_ptr,
                      const model::parameters& initial_parameters = model::parameters())
        : base_type(partition_distribution(initial_parameters),
                    partition_proposal(),
                    partition_sampler_sample(initial_partition_ptr, mh_moves::NONE, mh_moves::NONE))
    {
        // Merge partition sampler and MH sampler?
    }

    /// @brief The model parameters associated with this sampler.
    const model::parameters& parameters() const { return target_.params; }

    /// @brief Return a reference to the partition of the last sample drawn so far.
    const partition_ptr_t last_partition() const { return last_sample().partition_sample_ptr; }

    /// @brief Return a reference to the move type of the last sample drawn so far.
    const mh_moves::move_type& last_move() const { return last_sample().executed_move; }

    /// @brief Modify the parameters associated with this sampler.
    ///
    /// @param params A new set of parameters for the sampler.
    void set_parameters(const model::parameters& params) { target_.params = params; }

    /// @brief set the partition_is_valid value
    void set_partition_is_valid(bool v) { propose_.partition_is_valid = v; }
};

} // namespace biggles

#endif //BIGGLES_PARTITION_SAMPLER_HPP__


/*
// Copyright 2018 University of Liege
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Authors:
// - Adrien Crovato
*/

//// Field mapping
// Identify each cell as an interior or exterior (FLAG 0, 1). Also create vector of indexes associated to
// field mapping type.
// Crossing number method is used as point in polygon algorithm.
//
// References: http://geomalgorithms.com/a03-_inclusion.html
//
// I/O:
// - sGrid: temporary dynamic array containing body panel vertices
// - numC: list of numerical parameters (structure)
// - bPan: body panels (structure)
// - fPan: field panels (structure)

#include <iostream>
#include <Eigen/Dense>
#include "map_field.h"

#define NDIM 3

using namespace std;
using namespace Eigen;

void map_field(MatrixX3d &sGrid, Numerical_CST &numC, Network &bPan, Field &fPan) {

    //// Begin
    // Temporary variables
    double minX, maxX, minY, maxY, minZ, maxZ; // minimal bounding box holding the geometry
    int iE = 0, iI = 0; // indexes
    MatrixX2d panVert;
    panVert.resize(bPan.nC,2);

    // Display check
    cout << "Mapping field cells... " << flush;

    // Resize matrices
    fPan.fMap.resize(fPan.nF);

    // Compute minimum size box including full geometry
    minX = sGrid.col(0).minCoeff() - numC.TOLB;
    maxX = sGrid.col(0).maxCoeff() + numC.TOLB;
    minY = sGrid.col(1).minCoeff();
    maxY = sGrid.col(1).maxCoeff();
    minZ = sGrid.col(2).minCoeff() - numC.TOLB;
    maxZ = sGrid.col(2).maxCoeff() + numC.TOLB;

    //// Map field cells
    for (int f = 0; f < fPan.nF; f++) {
        lbl_loop0:
        // Bounding box
        if (fPan.CG(f,0) < minX || fPan.CG(f,0) > maxX
            || fPan.CG(f,1) < minY || fPan.CG(f,1) > maxY
            || fPan.CG(f,2) < minZ || fPan.CG(f,2) > maxZ) {
            fPan.fMap(f) = 1;
            fPan.nE++;
        }
        // 2D PIP algorithm
        else {
            // Find spanwise station corresponding to y-coordinate of field cell
            for (int s = 0; s < bPan.nS_; s++) {
                if (fPan.CG(f,1) <= sGrid((s+1)*bPan.nC,1)) {
                    // Interpolate
                    double a = (sGrid((s+1)*bPan.nC,1) - fPan.CG(f,1)) / (sGrid((s+1)*bPan.nC,1) - sGrid(s*bPan.nC,1));
                    double b = (fPan.CG(f,1) - sGrid(s*bPan.nC,1)) / (sGrid((s+1)*bPan.nC,1) - sGrid(s*bPan.nC,1));
                    // Store interpolated airfoil panel vertices
                    for (int i = 0; i < bPan.nC; i++) {
                        panVert(i,0) = a*sGrid(s*bPan.nC+i,0) + b*sGrid((s+1)*bPan.nC+i,0);
                        panVert(i,1) = a*sGrid(s*bPan.nC+i,2) + b*sGrid((s+1)*bPan.nC+i,2);
                    }
                    // Crossing number method
                    int nInter = 0;
                    for (int i = 0; i < bPan.nC_; i++) {
                        // Check if point is not on a vertex
                        if (fPan.CG(f,0) == panVert(i,0) && fPan.CG(f,2) == panVert(i,1)) {
                            fPan.fMap(f) = 0;
                            fPan.nI++;
                            f++;
                            goto lbl_loop0; // *might wanna use goto here!!
                        }
                        // Exclude top endpoint of segment to avoid double crossings and horizontal edges
                        if ((fPan.CG(f,2) >= panVert(i,1) && fPan.CG(f,2) < panVert(i+1,1))
                            || (fPan.CG(f,2) < panVert(i,1) && fPan.CG(f,2) >= panVert(i+1,1))) {
                            double sI = (fPan.CG(f,2) - panVert(i,1)) / (panVert(i+1,1) - panVert(i,1)); // Compute y-intersection
                            if (fPan.CG(f,0) <  panVert(i,0) + sI * (panVert(i+1,0) - panVert(i,0))) { // If point is left of x-intersect
                                nInter++; // Then intersection is valid
                            }
                        }
                    }
                    // Even number of intersections, external point
                    if (nInter%2 == 0) {
                        fPan.fMap(f) = 1;
                        fPan.nE++;
                        break;
                    }
                    // Odd number of intersections, internal point
                    else {
                        fPan.fMap(f) = 0;
                        fPan.nI++;
                        break;
                    }
                }
                else
                    continue;
            }
        }
    }
    // Find cell indices according to mapping type
    fPan.eIdx.resize(fPan.nE);
    fPan.iIdx.resize(fPan.nI);
    for (int f = 0; f < fPan.nF; ++f) {
        if (fPan.fMap(f)) {
            fPan.eIdx(iE) = f;
            iE++;
        }
        else {
            fPan.iIdx(iI) = f;
            iI++;
        }
    }

    //// Control display
    cout << "Done!" << endl;
    cout << "Field map: " << fPan.fMap.size() << endl;
    #ifdef VERBOSE
        for (int j = 0; j < fPan.nY; ++j) {
            cout << endl;
            for (int k = 0; k < fPan.nZ; ++k) {
                cout << endl;
                for (int i = 0; i < fPan.nX; ++i)
                    cout << fPan.fMap(i + k * fPan.nX + j * fPan.nX * fPan.nZ) << ' ';
            }
        }
        cout << endl;
    #endif
    cout << "Exterior cells: " << fPan.eIdx.rows() << endl;
    cout << "Interior cells: " << fPan.iIdx.rows() << endl;
    cout << endl;
}


// Copyright (c) 2015-2020 Daniel Cooke
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include "coalescent_model.hpp"

#include <memory>
#include <cmath>
#include <complex>
#include <numeric>
#include <stdexcept>

#include <boost/math/special_functions/binomial.hpp>

#include "utils/maths.hpp"

namespace octopus {

CoalescentModel::CoalescentModel(Haplotype reference, Parameters params,
                                 std::size_t num_haplotyes_hint, CachingStrategy caching)
: reference_ {std::move(reference)}
, indel_heterozygosity_model_ {make_indel_model(reference_, {params.indel_heterozygosity})}
, params_ {params}
, haplotypes_ {}
, caching_ {caching}
, index_cache_ {}
, index_flag_buffer_ {}
, k_indel_zero_result_cache_ {2 * num_haplotyes_hint, std::vector<boost::optional<LogProbability>> {}}
{
    if (params_.snp_heterozygosity <= 0 || params_.indel_heterozygosity <= 0) {
        throw std::domain_error {"CoalescentModel: snp and indel heterozygosity must be > 0"};
    }
    site_buffer1_.reserve(128);
    site_buffer2_.reserve(128);
    if (caching == CachingStrategy::address) {
        difference_address_cache_.reserve(num_haplotyes_hint);
    } else if (caching_ == CachingStrategy::value) {
        difference_value_cache_.reserve(num_haplotyes_hint);
        difference_value_cache_.emplace(std::piecewise_construct,
                                        std::forward_as_tuple(reference_),
                                        std::forward_as_tuple());
    }
    k_indel_pos_result_cache_.reserve(2 * num_haplotyes_hint);
}

void CoalescentModel::set_reference(Haplotype reference)
{
    reference_ = std::move(reference);
    if (caching_ == CachingStrategy::address) {
        difference_address_cache_.clear();
    } else if (caching_ == CachingStrategy::value) {
        difference_value_cache_.clear();
        difference_value_cache_.emplace(std::piecewise_construct,
                                        std::forward_as_tuple(reference_),
                                        std::forward_as_tuple());
    }
}

void CoalescentModel::prime(MappableBlock<Haplotype> haplotypes)
{
    haplotypes_ = std::move(haplotypes);
    index_cache_.assign(haplotypes_.size(), boost::none);
    index_flag_buffer_.assign(haplotypes_.size(), false);
}

void CoalescentModel::unprime() noexcept
{
    haplotypes_.clear();
    haplotypes_.shrink_to_fit();
    index_cache_.clear();
    index_cache_.shrink_to_fit();
    index_flag_buffer_.clear();
    index_flag_buffer_.shrink_to_fit();
}

bool CoalescentModel::is_primed() const noexcept
{
    return !index_cache_.empty();
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const Haplotype& haplotype) const
{
    return evaluate(count_segregating_sites(haplotype));
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const std::vector<unsigned>& haplotype_indices) const
{
    return evaluate(count_segregating_sites(haplotype_indices));
}

namespace {

auto powm1(const unsigned i) noexcept // std::pow(-1, i)
{
    return (i % 2 == 0) ? 1 : -1;
}

auto binom(const unsigned n, const unsigned k)
{
    return boost::math::binomial_coefficient<CoalescentModel::LogProbability>(n, k);
}

auto log_binom(const unsigned n, const unsigned k)
{
    using T = CoalescentModel::LogProbability;
    using maths::log_factorial;
    return log_factorial<T>(n) - (log_factorial<T>(k) + log_factorial<T>(n - k));
}

template <typename T>
auto coalescent_real_space(const unsigned n, const unsigned k, const T theta)
{
    T result {0};
    for (unsigned i {2}; i <= n; ++i) {
        result += powm1(i) * binom(n - 1, i - 1) * ((i - 1) / (theta + i - 1)) * std::pow(theta / (theta + i - 1), k);
    }
    return std::log(result);
}

template <typename ForwardIt>
auto complex_log_sum_exp(ForwardIt first, ForwardIt last)
{
    using ComplexType = typename std::iterator_traits<ForwardIt>::value_type;
    const auto l = [] (const auto& lhs, const auto& rhs) { return lhs.real() < rhs.real(); };
    const auto max = *std::max_element(first, last, l);
    return max + std::log(std::accumulate(first, last, ComplexType {},
                                          [max] (const auto curr, const auto x) { return curr + std::exp(x - max); }));
}

template <typename Container>
auto complex_log_sum_exp(const Container& logs)
{
    return complex_log_sum_exp(std::cbegin(logs), std::cend(logs));
}

template <typename T>
auto coalescent_log_space(const unsigned n, const unsigned k, const T theta)
{
    std::vector<std::complex<T>> tmp(n - 1, std::log(std::complex<T> {-1}));
    for (unsigned i {2}; i <= n; ++i) {
        auto& cur = tmp[i - 2];
        cur *= i;
        cur += log_binom(n - 1, i - 1);
        cur += std::log((i - 1) / (theta + i - 1));
        cur += k * std::log(theta / (theta + i - 1));
    }
    return complex_log_sum_exp(tmp).real();
}

template <typename T>
auto coalescent(const unsigned n, const unsigned k, const T theta)
{
    if (n < 30 && k <= 80) {
        auto result = coalescent_real_space(n, k, theta);
        if (std::isnan(result)) {
            result = coalescent_log_space(n, k, theta);
        }
        return result;
    } else {
        return coalescent_log_space(n, k, theta);
    }
}

template <typename T>
auto coalescent(const unsigned n, const unsigned k_snp, const unsigned k_indel,
                const T theta_snp, const T theta_indel)
{
    const auto theta = theta_snp + theta_indel;
    const auto k_tot = k_snp + k_indel;
    auto result = coalescent(n, k_tot, theta);
    result += k_snp * std::log(theta_snp / theta);
    result += k_indel * std::log(theta_indel / theta);
    result += log_binom(k_tot, k_snp);
    return result;
}

} // namespace

CoalescentModel::LogProbability CoalescentModel::evaluate(const SiteCountTuple& t) const
{
    unsigned k_snp, k_indel, n;
    std::tie(k_snp, k_indel, n) = t;
    if (k_indel == 0) {
        return evaluate(k_snp, n);
    } else {
        return evaluate(k_snp, k_indel, n);
    }
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const unsigned k_snp, const unsigned n) const
{
    if (k_indel_zero_result_cache_.size() > n) {
        if (k_indel_zero_result_cache_[n].size() > k_snp) {
            auto& result = k_indel_zero_result_cache_[n][k_snp];
            if (!result) {
                result = coalescent(n, k_snp, 0, params_.snp_heterozygosity, params_.indel_heterozygosity);
            }
            return *result;
        } else {
            k_indel_zero_result_cache_[n].resize(k_snp + 1, boost::none);
        }
    } else {
        k_indel_zero_result_cache_.resize(n + 1);
        k_indel_zero_result_cache_[n].assign(k_snp + 1, boost::none);
    }
    const auto result = coalescent(n, k_snp, 0, params_.snp_heterozygosity, params_.indel_heterozygosity);
    k_indel_zero_result_cache_[n][k_snp] = result;
    return result;
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const unsigned k_snp, const unsigned k_indel, const unsigned n) const
{
    const auto indel_heterozygosity = calculate_buffered_indel_heterozygosity();
    const auto t = std::make_tuple(k_snp, k_indel, n, maths::round_sf(indel_heterozygosity, 6));
    auto itr = k_indel_pos_result_cache_.find(t);
    if (itr != std::cend(k_indel_pos_result_cache_)) {
        return itr->second;
    }
    const auto result = coalescent(n, k_snp, k_indel, params_.snp_heterozygosity, indel_heterozygosity);
    k_indel_pos_result_cache_.emplace(t, result);
    return result;
}

void CoalescentModel::fill_site_buffer(const Haplotype& haplotype) const
{
    assert(site_buffer2_.empty());
    site_buffer1_.clear();
    if (caching_ == CachingStrategy::address) {
        fill_site_buffer_from_address_cache(haplotype);
    } else {
        fill_site_buffer_from_value_cache(haplotype);
    }
    site_buffer1_ = std::move(site_buffer2_);
    site_buffer2_.clear();
}

void CoalescentModel::fill_site_buffer_uncached(const Haplotype& haplotype) const
{
    // Although we won't retrieve from the cache, we need to make sure all the variants
    // stay in existence as we populate the buffers by reference.
    auto itr = difference_value_cache_.find(reference_);
    if (itr == std::cend(difference_value_cache_)) {
        itr = difference_value_cache_.emplace(reference_, haplotype.difference(reference_)).first;
    } else {
        itr->second = haplotype.difference(reference_);
    }
    std::set_union(std::begin(site_buffer1_), std::end(site_buffer1_),
                   std::cbegin(itr->second), std::cend(itr->second),
                   std::back_inserter(site_buffer2_));
}

void CoalescentModel::fill_site_buffer_from_value_cache(const Haplotype& haplotype) const
{
    auto itr = difference_value_cache_.find(haplotype);
    if (itr == std::cend(difference_value_cache_)) {
        itr = difference_value_cache_.emplace(haplotype, haplotype.difference(reference_)).first;
    }
    std::set_union(std::begin(site_buffer1_), std::end(site_buffer1_),
                   std::cbegin(itr->second), std::cend(itr->second),
                   std::back_inserter(site_buffer2_));
}

void CoalescentModel::fill_site_buffer_from_address_cache(const Haplotype& haplotype) const
{
    auto itr = difference_address_cache_.find(std::addressof(haplotype));
    if (itr == std::cend(difference_address_cache_)) {
        itr = difference_address_cache_.emplace(std::addressof(haplotype), haplotype.difference(reference_)).first;
    }
    std::set_union(std::begin(site_buffer1_), std::end(site_buffer1_),
                   std::cbegin(itr->second), std::cend(itr->second),
                   std::back_inserter(site_buffer2_));
}

CoalescentModel::SiteCountTuple CoalescentModel::count_segregating_sites(const Haplotype& haplotype) const
{
    fill_site_buffer(haplotype);
    return count_segregating_sites_in_buffer(1);
}

CoalescentModel::SiteCountTuple CoalescentModel::count_segregating_sites_in_buffer(const unsigned num_haplotypes) const
{
    const auto num_indels = std::count_if(std::cbegin(site_buffer1_), std::cend(site_buffer1_),
                                          [] (const auto& v) noexcept { return is_indel(v); });
    return std::make_tuple(site_buffer1_.size() - num_indels, num_indels, num_haplotypes + 1);
}

double CoalescentModel::calculate_buffered_indel_heterozygosity() const
{
    boost::optional<double> max_heterozygosity {};
    for (const auto& site : site_buffer1_) {
        if (is_indel(site)) {
            auto site_heterozygosity = calculate_heterozygosity(site);
            if (max_heterozygosity) {
                max_heterozygosity = std::max(*max_heterozygosity, site_heterozygosity);
            } else {
                max_heterozygosity = site_heterozygosity;
            }
        }
    }
    return max_heterozygosity ? *max_heterozygosity : params_.indel_heterozygosity;
}

double CoalescentModel::calculate_heterozygosity(const Variant& indel) const
{
    assert(is_indel(indel));
    const auto offset = static_cast<std::size_t>(begin_distance(reference_, indel));
    return calculate_indel_probability(indel_heterozygosity_model_, offset, indel_size(indel));
}

CoalescentProbabilityGreater::CoalescentProbabilityGreater(CoalescentModel model)
: model_ {std::move(model)}
, buffer_ {}
, cache_ {}
{
    buffer_.reserve(1);
    cache_.reserve(100);
}

bool CoalescentProbabilityGreater::operator()(const Haplotype& lhs, const Haplotype& rhs) const
{
    if (have_same_alleles(lhs, rhs)) return true;
    auto cache_itr = cache_.find(lhs);
    if (cache_itr == std::cend(cache_)) {
        buffer_.assign({lhs});
        cache_itr = cache_.emplace(lhs, model_.evaluate(buffer_)).first;
    }
    const auto lhs_probability = cache_itr->second;
    cache_itr = cache_.find(rhs);
    if (cache_itr == std::cend(cache_)) {
        buffer_.assign({rhs});
        cache_itr = cache_.emplace(rhs, model_.evaluate(buffer_)).first;
    }
    const auto rhs_probability = cache_itr->second;
    return lhs_probability > rhs_probability;
}

} // namespace octopus


#include <iostream>
#include <fstream>
#include <Eigen/Eigen>
//include the bie header files
#include "material.hh"
#include "precomputed_kernel.hh"
#include "bimat_interface.hh"
#include "infinite_boundary.hh"
//include the fem header files
//#include "mesh_Generated.hpp"
#include "mesh_Generated_multi_faults.hpp"

#include "bcdof.hpp"
#include "bcdof_ptr.hpp"
#include "cal_ke.hpp"
#include "cal_fe_global_const_ke.hpp"
#include "mapglobal.hpp"
#include "Slip_Weakening.hpp"
#include "Rate_and_State_Aging.hpp"
#include "cal_slip_sliprate.hpp"
#include "update_disp_velocity.hpp"
#include "time_advance.hpp"
#include "BIE_correct.hpp"

using namespace Eigen;
using namespace std;

int main() {
    // Domain Size
    double x_min = -20e3;
    double x_max = 20e3;
    double y_min = -1.0e3;
    double y_max = 1.0e3;
    int dim = 2.0;
    double dx = 50;
    double dy = 50;
    int nx_el = (x_max-x_min)/dx;
    int ny = (y_max-y_min)/dy;
    MatrixXd Node = MatrixXd::Zero((nx_el+1)*(ny+1),2);
    MatrixXi_rm Element(nx_el*ny,4); Element.setZero();
    ArrayXi BIE_top_surf_nodes = ArrayXi::Zero((nx_el+1),1);
    ArrayXi BIE_bot_surf_nodes = ArrayXi::Zero((nx_el+1),1);
    int num_faults = 1;
    // Mesh
    // Position of the fault : y_pos, x_left, x_right
    MatrixXd fault_pos(num_faults,3);
    fault_pos << 0.0,  x_min ,x_max;

    //    MatrixXd fault_pos;
    //    read_matrix("fault_pos.txt", fault_pos);
    //    int num_faults = fault_pos.rows();
    cout<<"test_mat="<<"\n"<<fault_pos<<"\n"<<"rows="<<fault_pos.rows()<<"\n";
    std::vector<std::vector<int>> fault_nodes(num_faults*2);
    mesh_Generated_multi_faults(x_min,x_max,y_min,y_max,dx,dy,nx_el,ny,Node, Element,BIE_top_surf_nodes, BIE_bot_surf_nodes,fault_pos, fault_nodes);

     double Vw_width = 5e3;
    // Finding Nodes on the diagonal
    ArrayXi left_diag_nodes =ArrayXi::Zero((ny+1),1);
    ArrayXi right_diag_nodes =ArrayXi::Zero((ny+1),1); ;
    double TOL = 1e-6;
    int m = 0;
    int n = 0;
    for(int i=0; i<Node.rows(); i++)
    {
        if ((std::abs(Node(i,0) +Vw_width/2.0)<TOL)&&(std::abs(Node(i,1))>=dx))
        {
            left_diag_nodes(m) = i ;
            m+=1;
        };
        if ((std::abs(Node(i,0) - Vw_width/2.0)<TOL)&&(std::abs(Node(i,1))>=dx))
        {
            right_diag_nodes(n) = i ;
            n+=1;
        };
    }
    VectorXi left_diag_index = VectorXi::Zero(2*(left_diag_nodes.size()),1);
    VectorXi right_diag_index = VectorXi::Zero(2*(right_diag_nodes.size()),1);

    bcdof(left_diag_nodes,dim,left_diag_index);
    bcdof(right_diag_nodes,dim,right_diag_index);

//    std::cout<<left_diag_nodes<<std::endl;
//    std::cout<<"********"<<std::endl;
//    std::cout<<right_diag_nodes<<std::endl;

    std::ofstream Element_output("results/Element.txt");
    std::ofstream Node_output("results/Node.txt");
    Node_output<<Node;
    Element_output<<Element;

    Element_output.close();
    Node_output.close();
    // Vector containing number of elements on each faults (nx)
    std::vector<int> nx_faults(num_faults);
    for (int i=0;i<num_faults;i++)
    {
        nx_faults[i] =fault_nodes[2*i].size();
    }

    // Write the x coordiantes for each fault
    std::vector<double> fault_angle(num_faults);
    double x1 = 0.0;
    double x2 = 0.0;
    double y1 = 0.0;
    double y2 = 0.0;
    for (int j=0;j<num_faults; j++)
    {
        VectorXd x = VectorXd::Zero(nx_faults[j], 1);
        VectorXd y = VectorXd::Zero(nx_faults[j], 1);

        for (int i=0; i<x.size(); i++)
        {
            x(i) = Node(fault_nodes[2*j][i],0);
            y(i) = Node(fault_nodes[2*j][i],1);
        }

        //std::ofstream x_output("results/fault_x_coord.txt");
        std::string  x_fault= "results/x_fault_"+std::to_string(j)+".txt";
        std::ofstream x_output(x_fault);
        x_output<<x;

        x1 = Node(fault_nodes[2*j][0],0);
        x2 = Node(fault_nodes[2*j][nx_faults[j]-1],0);
        y1 = Node(fault_nodes[2*j][0],1);
        y2 = Node(fault_nodes[2*j][nx_faults[j]-1],1);
        fault_angle[j]=std::atan2(y2-y1,x2-x1);
    }
    int n_nodes = Node.rows();
    int n_el = Element.rows();
    int Ndofn = 2;
    int Nnel = Element.cols();
   // nx_el = fault_nodes.size()-1;
    // Material
    double density = 2670.0;
    double v_s =3.464e3;
    double v_p = 6.0e3;
    double G= pow(v_s,2)*density;
    double Lambda = pow(v_p,2)*density-2.0*G;
    double E  = G*(3.0*Lambda+2.0*G)/(Lambda+G);
    double nu = Lambda/(2.0*(Lambda+G));
    // Time
    double alpha = 0.1;
    double dt = alpha*dx/v_p;
    // Reyleigh Damping
    double beta =0.2;
    double q = beta*dt;
    double time_run = 9.0;
    int numt = time_run/dt;
    //numt =1;

    std::ofstream time_output("results/time.txt");
    time_output<<time_run<<"\n";
    time_output<<dt<<"\n";
    time_output<<numt<<std::endl;
    //numt = 3;
    VectorXd time = dt*VectorXd::LinSpaced(numt,1,numt);
    // Rate and state friction parameters

    double a = 0.008;
    double b = 0.012;

    VectorXd a_array = a*VectorXd::Zero(nx_el+1,1);
    VectorXd b_array = b*VectorXd::Zero(nx_el+1,1);

    double V_0 = 1e-6;
    double f_0 = 0.6;
    double L = 0.02;
    double tau_ini = 75e6;
    double sigma_ini = 120e6;
    double V_ini  = 1e-12;
    // Intialization
    // disp velocity current and next time step (new)
    VectorXd u_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd u_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd a_n = VectorXd::Zero(n_nodes*Ndofn,1);
    // slip and slip-rate
    VectorXd delt_u_n = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd delt_v_n = VectorXd::Zero(Ndofn*(nx_el+1),1);
    std::cout<<delt_u_n.size()<<std::endl;
    std::cout<<nx_el<<std::endl;
    // Stress on the fault T_0 = intial stress , T = sticking force, T_c= stress critical goes into F_global
    VectorXd T_0 = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd T = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd T_c = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd tau_s = VectorXd::Zero(nx_el+1,1);
    VectorXd F_ext_global = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd theta_n =VectorXd::Zero(nx_el+1, 1);
    VectorXd theta_dot_n = VectorXd::Zero(nx_el+1, 1);
    VectorXd theta_ini =VectorXd::Zero(nx_el+1, 1);

    // Setting intial stress on the fault
    for (int i=0; i<T_0.size()/2; i++)
    {
        T_0(2*i) = tau_ini;
        T_0(2*i+1) = -sigma_ini;
    }
    VectorXd x = VectorXd::LinSpaced(nx_el+1,x_min,x_max);
    double d_tau_0 = 25e6;
    double R = 2.5e3;
    VectorXd F_r = VectorXd::Zero(nx_el+1,1);
    for (int i=0; i<nx_el+1; i++)
    {
        if (fabs(x(i))<R)
        {
            F_r(i) = exp(pow(x(i),2)/( pow(x(i),2)-pow(R,2)));
        }
        else
        {
            F_r(i) = 0.0;
        }
    }
    VectorXd G_t = VectorXd::Zero(numt,1);
    double T_ramp = 1.0;
    for (int i=0; i<numt;i++)
    {
        if(time(i)<T_ramp)
        {
            G_t(i) = exp(pow((time(i)-T_ramp),2)/(time(i)*(time(i)-2*T_ramp)));
        }
        else
        {
            G_t(i) = 1.0;
        }
    }

    for (int i =0; i<nx_el+1; i++){
        if (abs(x(i))<Vw_width/2.0)
        {
            a_array(i) = 0.008;
            b_array(i) = 0.012;
            theta_ini(i) = L/V_0*exp((a_array(i)*log(2*sinh(tau_ini/(a_array(i)*sigma_ini)))-f_0-a_array(i)*log(V_ini/V_0))/b_array(i));
        }
        else
        {
            b_array(i) = 0.08;
            a_array(i) = 0.12;
            theta_ini(i) = L/V_0*exp((a_array(i)*log(2*sinh(tau_ini/(a_array(i)*sigma_ini)))-f_0-a_array(i)*log(V_ini/V_0))/b_array(i));
        }
    }

    theta_n = theta_ini;
    MatrixXd d_tau = MatrixXd::Zero(nx_el+1,numt);
    for (int i=0;i<numt;i++)
    {
        d_tau.col(i)=75e6*VectorXd::Ones(nx_el+1,1)+d_tau_0*F_r*(G_t(i));
       // d_tau.col(i)=75e6*VectorXd::Ones(nx_el+1,1)+3.75e6*F_r+dt*0.3125e7*i*VectorXd::Ones(nx_el+1,1);
    }
    // Get the index degree of freedome for each element
    VectorXi index_el = VectorXi::Zero(Ndofn*Nnel,1);
    MatrixXi index_store = MatrixXi::Zero(Nnel*Ndofn,n_el);
    for (int i=0;i<n_el;i++)
    {
        bcdof(Element.row(i),dim,index_el);
        index_store.col(i) = index_el;
    }
    VectorXi BIE_top_surf_index = VectorXi::Zero(Ndofn*(BIE_top_surf_nodes.size()),1);
    VectorXi BIE_bot_surf_index = VectorXi::Zero(Ndofn*(BIE_bot_surf_nodes.size()),1);

    std::vector<Eigen::ArrayXi> Fault_surf_index(num_faults*2);
    for (int i=0; i <num_faults*2; i++)
    {
        Fault_surf_index[i] = ArrayXi::Zero(Ndofn*(fault_nodes[i].size()),1);
    }

    // Getting the faults DOF index
    for (int i=0; i<num_faults*2;i++)
    {
        bcdof_ptr(fault_nodes[i], dim, Fault_surf_index[i].data());
    }
    bcdof(BIE_top_surf_nodes,dim,BIE_top_surf_index);
    bcdof(BIE_bot_surf_nodes,dim,BIE_bot_surf_index);

    // Rate and state initlaized v_n delt_v_n
    for (int i=0;i<nx_el+1;i++)
    {
        delt_v_n(2*i)= V_ini;
  //      v_n(top_surf_index(2*i))= V_ini/2.0;

  //      v_n(bot_surf_index(2*i))= -V_ini/2.0;

        v_n(Fault_surf_index[0](2*i))= V_ini/2.0;

        v_n(Fault_surf_index[1](2*i))= -V_ini/2.0;
    }

    // Calculating the Global Mass Vector (lumped mass)
    // Element mass
    double M=density*dx*dy*1.0;
    VectorXd M_el_vec = M/4*VectorXd::Ones(Nnel*Ndofn,1);
    VectorXd M_global_vec=VectorXd::Zero(n_nodes*Ndofn,1);
    for (int i=0 ; i<n_el;i++)
    {
        index_el = index_store.col(i);
        mapglobal(index_el,M_global_vec,M_el_vec);
    }
    // Element matrix
    MatrixXd ke = MatrixXd::Zero(8,8);
    MatrixXd coord = MatrixXd::Zero(4,2);
    VectorXi Element_0= Element.row(0);
    coord.row(0) = Node.row(Element_0(0));
    coord.row(1) = Node.row(Element_0(1));
    coord.row(2) = Node.row(Element_0(2));
    coord.row(3) = Node.row(Element_0(3));

    std::cout<<coord<<std::endl;

    cal_ke (coord,E,nu,ke);
    // BIE part initiation
    // Setting up the material property for the BIE code
    Material BIE_top_mat = Material(E,nu,density);
    Material BIE_bot_mat = Material(E,nu,density);
    double length = x_max-x_min;
    // infinte bc BIE call infinite_boundary.cc
    PrecomputedKernel h11("kernels/nu_.25_h11.dat");
    PrecomputedKernel h12("kernels/nu_.25_k12.dat");
    PrecomputedKernel h22("kernels/nu_.25_h22.dat");
    InfiniteBoundary BIE_inf_top(length,nx_el+1,1.0,&BIE_top_mat,&h11,&h12,&h22);
    InfiniteBoundary BIE_inf_bot(length,nx_el+1,-1.0,&BIE_bot_mat,&h11,&h12,&h22);
    // BIE setting time step
    BIE_inf_top.setTimeStep(dt);
    BIE_inf_bot.setTimeStep(dt);
    // BIE initialization
    BIE_inf_top.init();
    BIE_inf_bot.init();

    //BIE_initiation(E, nu, density, x_max, x_min, nx_el, dt);
    printf("ready to start\n");
    // Output
    ofstream file;
    file.open("results/num_nodes_fault.bin",ios::binary);
    file.write((char*)(nx_faults.data()),nx_faults.size()*sizeof(int));
    file.close();
    //
    file.open("results/u_n.bin",ios::binary);
    file.close();
    file.open("results/v_n.bin",ios::binary);
    file.close();
    file.open("results/eq_ep_n.bin",ios::binary);
    file.close();

    for (int i=0;i<num_faults;i++)
    {
        std::string slip = "results/slip_"+std::to_string(i)+".bin";
        file.open(slip);
        file.close();
        std::string slip_rate = "results/slip_rate_"+std::to_string(i)+".bin";
        file.open(slip_rate);
        file.close();
        std::string shear = "results/shear_"+std::to_string(i)+".bin";
        file.open(shear);
        file.close();
    }

    // Main time loop
    for (int j=0;j<numt;j++)
    {
        // Compute the global internal force
        VectorXd fe_global= VectorXd::Zero(n_nodes*Ndofn,1);
        cal_fe_global_const_ke(n_nodes, n_el, index_store, q, u_n, v_n, Ndofn, ke, fe_global);
        // Friction subroutine
        VectorXd F_fault = VectorXd::Zero(Ndofn*(nx_el+1),1);
      //  Slip_Weakening(M_global_vec, top_surf_index, bot_surf_index, fe_global, dt, dx, dy, nx_el, delt_v_n, delt_u_n, T_0, tau_s, mu_s, mu_d, Dc, Ndofn, M, F_fault, T_c);
        for (int i=0;i<nx_el+1;i++)
        {
            T_0(2*i) = d_tau(i,j);
        }
        Rate_and_State_Aging(M_global_vec, Fault_surf_index[0], Fault_surf_index[1], fe_global, dt, dx, dy, nx_el, delt_u_n, delt_v_n, T_0, tau_s, a_array, b_array , L, V_0, f_0, Ndofn, M, F_fault, T_c, theta_n, theta_dot_n);

        // Calculate the global force vector
        // Adding contribution of the fault force to the global force vector F_total
        VectorXd F_total = F_ext_global-fe_global;
        mapglobal(Fault_surf_index[0],F_total,-F_fault);
        mapglobal(Fault_surf_index[1],F_total,F_fault);
        // Central Difference Time integration
        time_advance(u_n, v_n, F_total, M_global_vec, dt);

//        for (int i=0 ; i<left_diag_index.size();i++)
//        {
//            u_n(left_diag_index(i)) = 0.0;
//            v_n(left_diag_index(i)) = 0.0;
//            u_n(right_diag_index(i)) = 0.0;
//            v_n(right_diag_index(i)) = 0.0;
//        }
//

        // Get the slip and slip rate
        //cal_slip_slip_rate(u_n, v_n, top_surf_index, bot_surf_index, Ndofn, nx_el, delt_u_n, delt_v_n);
        update_disp_velocity(u_n, v_n, Fault_surf_index[0], Fault_surf_index[1], Ndofn, nx_el, delt_u_n, delt_v_n);

        // Correct the BIE surf nodes solutions from FEM with the BIE solution
        BIE_correct(BIE_top_surf_index, BIE_bot_surf_index, fe_global, Ndofn, nx_el, dx, BIE_inf_top, BIE_inf_bot, u_n, v_n);

        // Set the diagonal to be fixed

        //ofstream file;
        for (int i=0;i<1;i++)
        {
            std::string slip = "results/slip_"+std::to_string(i)+".bin";
            file.open(slip,ios::binary | ios::app);
            file.write((char*)(delt_u_n.data()),delt_u_n.size()*sizeof(double));
            file.close();

            std::string slip_rate = "results/slip_rate_"+std::to_string(i)+".bin";
            file.open(slip_rate,ios::binary | ios::app);
            file.write((char*)(delt_v_n.data()),delt_v_n.size()*sizeof(double));
            file.close();

            std::string shear = "results/shear_"+std::to_string(i)+".bin";
            file.open(shear,ios::binary | ios::app);
            file.write((char*)(T_c.data()),T_c.size()*sizeof(double));
            file.close();
        }

        if (j%100==1)
        {
            file.open("results/u_n.bin",ios::binary | ios::app);
            file.write((char*)(u_n.data()),u_n.size()*sizeof(double));
            file.close();
            file.open("results/v_n.bin",ios::binary | ios::app);
            file.write((char*)(v_n.data()),v_n.size()*sizeof(double));
            file.close();
        }

        printf("Simulation time = %f\n",time(j));

    }
    return 0;
}