186d7f5d3SJohn Marino /* mpz_congruent_p -- test congruence of two mpz's.
286d7f5d3SJohn Marino
386d7f5d3SJohn Marino Copyright 2001, 2002, 2005 Free Software Foundation, Inc.
486d7f5d3SJohn Marino
586d7f5d3SJohn Marino This file is part of the GNU MP Library.
686d7f5d3SJohn Marino
786d7f5d3SJohn Marino The GNU MP Library is free software; you can redistribute it and/or modify
886d7f5d3SJohn Marino it under the terms of the GNU Lesser General Public License as published by
986d7f5d3SJohn Marino the Free Software Foundation; either version 3 of the License, or (at your
1086d7f5d3SJohn Marino option) any later version.
1186d7f5d3SJohn Marino
1286d7f5d3SJohn Marino The GNU MP Library is distributed in the hope that it will be useful, but
1386d7f5d3SJohn Marino WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
1486d7f5d3SJohn Marino or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
1586d7f5d3SJohn Marino License for more details.
1686d7f5d3SJohn Marino
1786d7f5d3SJohn Marino You should have received a copy of the GNU Lesser General Public License
1886d7f5d3SJohn Marino along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
1986d7f5d3SJohn Marino
2086d7f5d3SJohn Marino #include "gmp.h"
2186d7f5d3SJohn Marino #include "gmp-impl.h"
2286d7f5d3SJohn Marino #include "longlong.h"
2386d7f5d3SJohn Marino
2486d7f5d3SJohn Marino
2586d7f5d3SJohn Marino /* For big divisors this code is only very slightly better than the user
2686d7f5d3SJohn Marino doing a combination of mpz_sub and mpz_tdiv_r, but it's quite convenient,
2786d7f5d3SJohn Marino and perhaps in the future can be improved, in similar ways to
2886d7f5d3SJohn Marino mpn_divisible_p perhaps.
2986d7f5d3SJohn Marino
3086d7f5d3SJohn Marino The csize==1 / dsize==1 special case makes mpz_congruent_p as good as
3186d7f5d3SJohn Marino mpz_congruent_ui_p on relevant operands, though such a combination
3286d7f5d3SJohn Marino probably doesn't occur often.
3386d7f5d3SJohn Marino
3486d7f5d3SJohn Marino Alternatives:
3586d7f5d3SJohn Marino
3686d7f5d3SJohn Marino If c<d then it'd work to just form a%d and compare a and c (either as
3786d7f5d3SJohn Marino a==c or a+c==d depending on the signs), but the saving from avoiding the
3886d7f5d3SJohn Marino abs(a-c) calculation would be small compared to the division.
3986d7f5d3SJohn Marino
4086d7f5d3SJohn Marino Similarly if both a<d and c<d then it would work to just compare a and c
4186d7f5d3SJohn Marino (a==c or a+c==d), but this isn't considered a particularly important case
4286d7f5d3SJohn Marino and so isn't done for the moment.
4386d7f5d3SJohn Marino
4486d7f5d3SJohn Marino Low zero limbs on d could be stripped and the corresponding limbs of a
4586d7f5d3SJohn Marino and c tested and skipped, but doing so would introduce a borrow when a
4686d7f5d3SJohn Marino and c differ in sign and have non-zero skipped limbs. It doesn't seem
4786d7f5d3SJohn Marino worth the complications to do this, since low zero limbs on d should
4886d7f5d3SJohn Marino occur only rarely. */
4986d7f5d3SJohn Marino
5086d7f5d3SJohn Marino int
mpz_congruent_p(mpz_srcptr a,mpz_srcptr c,mpz_srcptr d)5186d7f5d3SJohn Marino mpz_congruent_p (mpz_srcptr a, mpz_srcptr c, mpz_srcptr d)
5286d7f5d3SJohn Marino {
5386d7f5d3SJohn Marino mp_size_t asize, csize, dsize, sign;
5486d7f5d3SJohn Marino mp_srcptr ap, cp, dp;
5586d7f5d3SJohn Marino mp_ptr xp;
5686d7f5d3SJohn Marino mp_limb_t alow, clow, dlow, dmask, r;
5786d7f5d3SJohn Marino int result;
5886d7f5d3SJohn Marino TMP_DECL;
5986d7f5d3SJohn Marino
6086d7f5d3SJohn Marino dsize = SIZ(d);
6186d7f5d3SJohn Marino if (UNLIKELY (dsize == 0))
6286d7f5d3SJohn Marino return (mpz_cmp (a, c) == 0);
6386d7f5d3SJohn Marino
6486d7f5d3SJohn Marino dsize = ABS(dsize);
6586d7f5d3SJohn Marino dp = PTR(d);
6686d7f5d3SJohn Marino
6786d7f5d3SJohn Marino if (ABSIZ(a) < ABSIZ(c))
6886d7f5d3SJohn Marino MPZ_SRCPTR_SWAP (a, c);
6986d7f5d3SJohn Marino
7086d7f5d3SJohn Marino asize = SIZ(a);
7186d7f5d3SJohn Marino csize = SIZ(c);
7286d7f5d3SJohn Marino sign = (asize ^ csize);
7386d7f5d3SJohn Marino
7486d7f5d3SJohn Marino asize = ABS(asize);
7586d7f5d3SJohn Marino ap = PTR(a);
7686d7f5d3SJohn Marino
7786d7f5d3SJohn Marino if (csize == 0)
7886d7f5d3SJohn Marino return mpn_divisible_p (ap, asize, dp, dsize);
7986d7f5d3SJohn Marino
8086d7f5d3SJohn Marino csize = ABS(csize);
8186d7f5d3SJohn Marino cp = PTR(c);
8286d7f5d3SJohn Marino
8386d7f5d3SJohn Marino alow = ap[0];
8486d7f5d3SJohn Marino clow = cp[0];
8586d7f5d3SJohn Marino dlow = dp[0];
8686d7f5d3SJohn Marino
8786d7f5d3SJohn Marino /* Check a==c mod low zero bits of dlow. This might catch a few cases of
8886d7f5d3SJohn Marino a!=c quickly, and it helps the csize==1 special cases below. */
8986d7f5d3SJohn Marino dmask = LOW_ZEROS_MASK (dlow) & GMP_NUMB_MASK;
9086d7f5d3SJohn Marino alow = (sign >= 0 ? alow : -alow);
9186d7f5d3SJohn Marino if (((alow-clow) & dmask) != 0)
9286d7f5d3SJohn Marino return 0;
9386d7f5d3SJohn Marino
9486d7f5d3SJohn Marino if (csize == 1)
9586d7f5d3SJohn Marino {
9686d7f5d3SJohn Marino if (dsize == 1)
9786d7f5d3SJohn Marino {
9886d7f5d3SJohn Marino cong_1:
9986d7f5d3SJohn Marino if (sign < 0)
10086d7f5d3SJohn Marino NEG_MOD (clow, clow, dlow);
10186d7f5d3SJohn Marino
10286d7f5d3SJohn Marino if (ABOVE_THRESHOLD (asize, BMOD_1_TO_MOD_1_THRESHOLD))
10386d7f5d3SJohn Marino {
10486d7f5d3SJohn Marino r = mpn_mod_1 (ap, asize, dlow);
10586d7f5d3SJohn Marino if (clow < dlow)
10686d7f5d3SJohn Marino return r == clow;
10786d7f5d3SJohn Marino else
10886d7f5d3SJohn Marino return r == (clow % dlow);
10986d7f5d3SJohn Marino }
11086d7f5d3SJohn Marino
11186d7f5d3SJohn Marino if ((dlow & 1) == 0)
11286d7f5d3SJohn Marino {
11386d7f5d3SJohn Marino /* Strip low zero bits to get odd d required by modexact. If
11486d7f5d3SJohn Marino d==e*2^n then a==c mod d if and only if both a==c mod e and
11586d7f5d3SJohn Marino a==c mod 2^n, the latter having been done above. */
11686d7f5d3SJohn Marino unsigned twos;
11786d7f5d3SJohn Marino count_trailing_zeros (twos, dlow);
11886d7f5d3SJohn Marino dlow >>= twos;
11986d7f5d3SJohn Marino }
12086d7f5d3SJohn Marino
12186d7f5d3SJohn Marino r = mpn_modexact_1c_odd (ap, asize, dlow, clow);
12286d7f5d3SJohn Marino return r == 0 || r == dlow;
12386d7f5d3SJohn Marino }
12486d7f5d3SJohn Marino
12586d7f5d3SJohn Marino /* dlow==0 is avoided since we don't want to bother handling extra low
12686d7f5d3SJohn Marino zero bits if dsecond is even (would involve borrow if a,c differ in
12786d7f5d3SJohn Marino sign and alow,clow!=0). */
12886d7f5d3SJohn Marino if (dsize == 2 && dlow != 0)
12986d7f5d3SJohn Marino {
13086d7f5d3SJohn Marino mp_limb_t dsecond = dp[1];
13186d7f5d3SJohn Marino
13286d7f5d3SJohn Marino if (dsecond <= dmask)
13386d7f5d3SJohn Marino {
13486d7f5d3SJohn Marino unsigned twos;
13586d7f5d3SJohn Marino count_trailing_zeros (twos, dlow);
13686d7f5d3SJohn Marino dlow = (dlow >> twos) | (dsecond << (GMP_NUMB_BITS-twos));
13786d7f5d3SJohn Marino ASSERT_LIMB (dlow);
13886d7f5d3SJohn Marino
13986d7f5d3SJohn Marino /* dlow will be odd here, so the test for it even under cong_1
14086d7f5d3SJohn Marino is unnecessary, but the rest of that code is wanted. */
14186d7f5d3SJohn Marino goto cong_1;
14286d7f5d3SJohn Marino }
14386d7f5d3SJohn Marino }
14486d7f5d3SJohn Marino }
14586d7f5d3SJohn Marino
14686d7f5d3SJohn Marino TMP_MARK;
14786d7f5d3SJohn Marino xp = TMP_ALLOC_LIMBS (asize+1);
14886d7f5d3SJohn Marino
14986d7f5d3SJohn Marino /* calculate abs(a-c) */
15086d7f5d3SJohn Marino if (sign >= 0)
15186d7f5d3SJohn Marino {
15286d7f5d3SJohn Marino /* same signs, subtract */
15386d7f5d3SJohn Marino if (asize > csize || mpn_cmp (ap, cp, asize) >= 0)
15486d7f5d3SJohn Marino ASSERT_NOCARRY (mpn_sub (xp, ap, asize, cp, csize));
15586d7f5d3SJohn Marino else
15686d7f5d3SJohn Marino ASSERT_NOCARRY (mpn_sub_n (xp, cp, ap, asize));
15786d7f5d3SJohn Marino MPN_NORMALIZE (xp, asize);
15886d7f5d3SJohn Marino }
15986d7f5d3SJohn Marino else
16086d7f5d3SJohn Marino {
16186d7f5d3SJohn Marino /* different signs, add */
16286d7f5d3SJohn Marino mp_limb_t carry;
16386d7f5d3SJohn Marino carry = mpn_add (xp, ap, asize, cp, csize);
16486d7f5d3SJohn Marino xp[asize] = carry;
16586d7f5d3SJohn Marino asize += (carry != 0);
16686d7f5d3SJohn Marino }
16786d7f5d3SJohn Marino
16886d7f5d3SJohn Marino result = mpn_divisible_p (xp, asize, dp, dsize);
16986d7f5d3SJohn Marino
17086d7f5d3SJohn Marino TMP_FREE;
17186d7f5d3SJohn Marino return result;
17286d7f5d3SJohn Marino }
173