xref: /dflybsd-src/contrib/gmp/mpz/cong.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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