/* Copyright (C) 2021 Fredrik Johansson 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 "flint.h" #include "ulong_extras.h" #include "fmpz.h" static ulong _fmpz_gcd_big_small(const fmpz_t g, ulong h) { __mpz_struct * z = COEFF_TO_PTR(*g); return n_gcd(mpn_mod_1(z->_mp_d, FLINT_ABS(z->_mp_size), h), h); } static ulong _fmpz_gcd_small(const fmpz_t g, ulong h) { if (!COEFF_IS_MPZ(*g)) return n_gcd(FLINT_ABS(*g), h); else return _fmpz_gcd_big_small(g, h); } static void fmpz_gcd3_small(fmpz_t res, const fmpz_t a, const fmpz_t b, ulong c) { if (c <= 1) { if (c == 1) fmpz_one(res); else fmpz_gcd(res, a, b); } else { if (!COEFF_IS_MPZ(*a)) { c = n_gcd(FLINT_ABS(*a), c); if (c != 1) c = _fmpz_gcd_small(b, c); } else { c = _fmpz_gcd_small(b, c); if (c != 1) c = _fmpz_gcd_big_small(a, c); } fmpz_set_ui(res, c); } } void fmpz_gcd3(fmpz_t res, const fmpz_t a, const fmpz_t b, const fmpz_t c) { if (!COEFF_IS_MPZ(*a)) { fmpz_gcd3_small(res, b, c, FLINT_ABS(*a)); } else if (!COEFF_IS_MPZ(*b)) { fmpz_gcd3_small(res, a, c, FLINT_ABS(*b)); } else if (!COEFF_IS_MPZ(*c)) { fmpz_gcd3_small(res, a, b, FLINT_ABS(*c)); } else { /* Three-way mpz_gcd. */ __mpz_struct *rp, *ap, *bp, *cp, *tp; mp_size_t an, bn, cn, mn; /* If res is small, it cannot be aliased with a, b, c, so promoting is fine. */ rp = _fmpz_promote(res); ap = COEFF_TO_PTR(*a); bp = COEFF_TO_PTR(*b); cp = COEFF_TO_PTR(*c); an = FLINT_ABS(ap->_mp_size); bn = FLINT_ABS(bp->_mp_size); cn = FLINT_ABS(cp->_mp_size); /* Select c to be the largest operand; we do the smaller gcd first. */ mn = FLINT_MAX(FLINT_MAX(an, bn), cn); tp = cp; if (mn != cn) { if (mn == an) { cp = ap; ap = tp; } else { cp = bp; bp = tp; } cn = mn; } /* Handle aliasing */ if (rp == cp) { mpz_t t; TMP_INIT; TMP_START; /* It would be more efficient to allocate temporary space for gcd(a, b), but we can't be sure that mpz_gcd never attempts to reallocate the output. */ t->_mp_d = TMP_ALLOC(sizeof(mp_limb_t) * cn); t->_mp_size = t->_mp_alloc = cn; flint_mpn_copyi(t->_mp_d, cp->_mp_d, cn); mpz_gcd(rp, ap, bp); if (mpz_cmpabs_ui(rp, 1) != 0) mpz_gcd(rp, rp, t); TMP_END; } else { mpz_gcd(rp, ap, bp); if (mpz_cmpabs_ui(rp, 1) != 0) mpz_gcd(rp, rp, cp); } /* The result may be small */ _fmpz_demote_val(res); } }