/*
Copyright 1991, 1993-1996, 2000, 2001, 2005 Free Software Foundation, Inc.
Copyright (C) 2013 William Hart
This file is part of FLINT.
FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See .
*/
#include
#include
#include
#include "flint.h"
#include "longlong.h"
#include "ulong_extras.h"
#include "mpn_extras.h"
#include "fmpz.h"
/* these functions were adapted from similar functions in an old version of GMP */
void _mpz_tdiv_qr_preinvn(mpz_ptr q, mpz_ptr r,
mpz_srcptr a, mpz_srcptr d, const fmpz_preinvn_t inv)
{
slong size1 = a->_mp_size, size2 = d->_mp_size;
ulong usize1 = FLINT_ABS(size1);
ulong usize2 = FLINT_ABS(size2);
ulong qsize = usize1 - usize2 + 1;
int nm = (inv->norm != 0);
TMP_INIT;
mp_ptr qp, rp, ap, dp, tp, sp;
if (r->_mp_alloc < usize1 + nm)
mpz_realloc2(r, (usize1 + nm)*FLINT_BITS);
if (usize1 < usize2) /* special case preinv code can't deal with */
{
mpz_set(r, a); /* remainder equals numerator */
q->_mp_size = 0; /* quotient is zero */
return;
}
if (q->_mp_alloc < qsize + nm)
mpz_realloc2(q, (qsize + nm)*FLINT_BITS);
dp = d->_mp_d;
ap = a->_mp_d;
qp = q->_mp_d;
rp = r->_mp_d;
TMP_START;
if ((r == d || q == d) && !nm) /* we have alias with d */
{
tp = TMP_ALLOC(usize2*FLINT_BITS);
mpn_copyi(tp, dp, usize2);
dp = tp;
}
if (r == a || q == a) /* we have alias with a */
{
tp = TMP_ALLOC(usize1*FLINT_BITS);
mpn_copyi(tp, ap, usize1);
ap = tp;
}
/*
TODO: speedup mpir's mullow and mulhigh and use in
flint_mpn_divrem_preinvn so we can remove this first
case here
*/
if (usize2 == 2 || (usize2 > 15 && usize2 < 120))
mpn_tdiv_qr(qp, rp, 0, ap, usize1, dp, usize2);
else {
if (nm) {
tp = TMP_ALLOC(usize2*FLINT_BITS);
mpn_lshift(tp, dp, usize2, inv->norm);
dp = tp;
rp[usize1] = mpn_lshift(rp, ap, usize1, inv->norm);
if (rp[usize1] != 0) usize1++, qsize++;
sp = rp;
} else
sp = ap;
qp[qsize - 1] = flint_mpn_divrem_preinvn(qp, rp, sp, usize1, dp, usize2, inv->dinv);
if (nm)
mpn_rshift(rp, rp, usize2, inv->norm);
}
qsize -= (qp[qsize - 1] == 0);
MPN_NORM(rp, usize2);
q->_mp_size = ((size1 ^ size2) < 0 ? -qsize : qsize);
r->_mp_size = (size1 < 0 ? -usize2 : usize2);
TMP_END;
}
void _mpz_fdiv_qr_preinvn(mpz_ptr q, mpz_ptr r,
mpz_srcptr a, mpz_srcptr d, const fmpz_preinvn_t inv)
{
slong size1 = a->_mp_size;
slong size2 = d->_mp_size;
ulong usize2 = FLINT_ABS(size2);
mpz_t t;
TMP_INIT;
TMP_START;
if (q == d || r == d) /* we need d later, so make sure it doesn't alias */
{
t->_mp_d = TMP_ALLOC(usize2*FLINT_BITS);
t->_mp_size = d->_mp_size;
t->_mp_alloc = d->_mp_alloc;
mpn_copyi(t->_mp_d, d->_mp_d, usize2);
d = t;
}
_mpz_tdiv_qr_preinvn(q, r, a, d, inv);
if ((size1 ^ size2) < 0 && r->_mp_size != 0)
{
flint_mpz_sub_ui(q, q, 1);
mpz_add(r, r, d);
}
TMP_END;
}
void
fmpz_fdiv_qr_preinvn(fmpz_t f, fmpz_t s, const fmpz_t g,
const fmpz_t h, const fmpz_preinvn_t inv)
{
fmpz c1 = *g;
fmpz c2 = *h;
if (fmpz_is_zero(h))
{
flint_printf("Exception (fmpz_fdiv_q). Division by zero.\n");
flint_abort();
}
if (!COEFF_IS_MPZ(c1)) /* g is small */
{
if (!COEFF_IS_MPZ(c2)) /* h is also small */
fmpz_fdiv_qr(f, s, g, h);
else /* h is large and g is small */
{
if (c1 == WORD(0))
{
fmpz_set_ui(f, WORD(0)); /* g is zero */
fmpz_set_si(s, c1);
}
else if ((c1 < WORD(0) && fmpz_sgn(h) < 0) || (c1 > WORD(0) && fmpz_sgn(h) > 0)) /* signs are the same */
{
fmpz_zero(f); /* quotient is positive, round down to zero */
fmpz_set_si(s, c1);
}
else
{
fmpz_add(s, g, h);
fmpz_set_si(f, WORD(-1)); /* quotient is negative, round down to minus one */
}
}
}
else /* g is large */
{
__mpz_struct *mpz_ptr, *mpz_ptr2;
if (!COEFF_IS_MPZ(c2)) /* h is small */
fmpz_fdiv_qr(f, s, g, h);
else
{
_fmpz_promote(f); /* must not hang on to ptr whilst promoting s */
mpz_ptr2 = _fmpz_promote(s);
mpz_ptr = COEFF_TO_PTR(*f);
_mpz_fdiv_qr_preinvn(mpz_ptr, mpz_ptr2, COEFF_TO_PTR(c1), COEFF_TO_PTR(c2), inv);
_fmpz_demote_val(f); /* division by h may result in small value */
_fmpz_demote_val(s); /* division by h may result in small value */
}
}
}