xref: /netbsd-src/external/lgpl3/gmp/dist/mpn/generic/divis.c (revision 72c7faa4dbb41dbb0238d6b4a109da0d4b236dd4)
1 /* mpn_divisible_p -- mpn by mpn divisibility test
2 
3    THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY.  THEY'RE ALMOST
4    CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
5    FUTURE GNU MP RELEASES.
6 
7 Copyright 2001, 2002, 2005, 2009, 2014, 2017, 2018 Free Software
8 Foundation, Inc.
9 
10 This file is part of the GNU MP Library.
11 
12 The GNU MP Library is free software; you can redistribute it and/or modify
13 it under the terms of either:
14 
15   * the GNU Lesser General Public License as published by the Free
16     Software Foundation; either version 3 of the License, or (at your
17     option) any later version.
18 
19 or
20 
21   * the GNU General Public License as published by the Free Software
22     Foundation; either version 2 of the License, or (at your option) any
23     later version.
24 
25 or both in parallel, as here.
26 
27 The GNU MP Library is distributed in the hope that it will be useful, but
28 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
29 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
30 for more details.
31 
32 You should have received copies of the GNU General Public License and the
33 GNU Lesser General Public License along with the GNU MP Library.  If not,
34 see https://www.gnu.org/licenses/.  */
35 
36 #include "gmp-impl.h"
37 #include "longlong.h"
38 
39 
40 /* Determine whether A={ap,an} is divisible by D={dp,dn}.  Must have both
41    operands normalized, meaning high limbs non-zero, except that an==0 is
42    allowed.
43 
44    There usually won't be many low zero bits on D, but the checks for this
45    are fast and might pick up a few operand combinations, in particular they
46    might reduce D to fit the single-limb mod_1/modexact_1 code.
47 
48    Future:
49 
50    Getting the remainder limb by limb would make an early exit possible on
51    finding a non-zero.  This would probably have to be bdivmod style so
52    there's no addback, but it would need a multi-precision inverse and so
53    might be slower than the plain method (on small sizes at least).
54 
55    When D must be normalized (shifted to low bit set), it's possible to
56    suppress the bit-shifting of A down, as long as it's already been checked
57    that A has at least as many trailing zero bits as D.  */
58 
59 int
mpn_divisible_p(mp_srcptr ap,mp_size_t an,mp_srcptr dp,mp_size_t dn)60 mpn_divisible_p (mp_srcptr ap, mp_size_t an,
61 		 mp_srcptr dp, mp_size_t dn)
62 {
63   mp_limb_t  alow, dlow, dmask;
64   mp_ptr     qp, rp, tp;
65   mp_limb_t di;
66   unsigned  twos;
67   int c;
68   TMP_DECL;
69 
70   ASSERT (an >= 0);
71   ASSERT (an == 0 || ap[an-1] != 0);
72   ASSERT (dn >= 1);
73   ASSERT (dp[dn-1] != 0);
74   ASSERT_MPN (ap, an);
75   ASSERT_MPN (dp, dn);
76 
77   /* When a<d only a==0 is divisible.
78      Notice this test covers all cases of an==0. */
79   if (an < dn)
80     return (an == 0);
81 
82   /* Strip low zero limbs from d, requiring a==0 on those. */
83   for (;;)
84     {
85       alow = *ap;
86       dlow = *dp;
87 
88       if (dlow != 0)
89 	break;
90 
91       if (alow != 0)
92 	return 0;  /* a has fewer low zero limbs than d, so not divisible */
93 
94       /* a!=0 and d!=0 so won't get to n==0 */
95       an--; ASSERT (an >= 1);
96       dn--; ASSERT (dn >= 1);
97       ap++;
98       dp++;
99     }
100 
101   /* a must have at least as many low zero bits as d */
102   dmask = LOW_ZEROS_MASK (dlow);
103   if ((alow & dmask) != 0)
104     return 0;
105 
106   if (dn == 1)
107     {
108       if (ABOVE_THRESHOLD (an, BMOD_1_TO_MOD_1_THRESHOLD))
109 	return mpn_mod_1 (ap, an, dlow) == 0;
110 
111       count_trailing_zeros (twos, dlow);
112       dlow >>= twos;
113       return mpn_modexact_1_odd (ap, an, dlow) == 0;
114     }
115 
116   count_trailing_zeros (twos, dlow);
117   if (dn == 2)
118     {
119       mp_limb_t  dsecond = dp[1];
120       if (dsecond <= dmask)
121 	{
122 	  dlow = (dlow >> twos) | (dsecond << (GMP_NUMB_BITS-twos));
123 	  ASSERT_LIMB (dlow);
124 	  return MPN_MOD_OR_MODEXACT_1_ODD (ap, an, dlow) == 0;
125 	}
126     }
127 
128   /* Should we compute Q = A * D^(-1) mod B^k,
129                        R = A - Q * D  mod B^k
130      here, for some small values of k?  Then check if R = 0 (mod B^k).  */
131 
132   /* We could also compute A' = A mod T and D' = D mod P, for some
133      P = 3 * 5 * 7 * 11 ..., and then check if any prime factor from P
134      dividing D' also divides A'.  */
135 
136   TMP_MARK;
137 
138   TMP_ALLOC_LIMBS_2 (rp, an + 1,
139 		     qp, an - dn + 1); /* FIXME: Could we avoid this? */
140 
141   if (twos != 0)
142     {
143       tp = TMP_ALLOC_LIMBS (dn);
144       ASSERT_NOCARRY (mpn_rshift (tp, dp, dn, twos));
145       dp = tp;
146 
147       ASSERT_NOCARRY (mpn_rshift (rp, ap, an, twos));
148     }
149   else
150     {
151       MPN_COPY (rp, ap, an);
152     }
153   if (rp[an - 1] >= dp[dn - 1])
154     {
155       rp[an] = 0;
156       an++;
157     }
158   else if (an == dn)
159     {
160       TMP_FREE;
161       return 0;
162     }
163 
164   ASSERT (an > dn);		/* requirement of functions below */
165 
166   if (BELOW_THRESHOLD (dn, DC_BDIV_QR_THRESHOLD) ||
167       BELOW_THRESHOLD (an - dn, DC_BDIV_QR_THRESHOLD))
168     {
169       binvert_limb (di, dp[0]);
170       mpn_sbpi1_bdiv_qr (qp, rp, an, dp, dn, -di);
171       rp += an - dn;
172     }
173   else if (BELOW_THRESHOLD (dn, MU_BDIV_QR_THRESHOLD))
174     {
175       binvert_limb (di, dp[0]);
176       mpn_dcpi1_bdiv_qr (qp, rp, an, dp, dn, -di);
177       rp += an - dn;
178     }
179   else
180     {
181       tp = TMP_ALLOC_LIMBS (mpn_mu_bdiv_qr_itch (an, dn));
182       mpn_mu_bdiv_qr (qp, rp, rp, an, dp, dn, tp);
183     }
184 
185   /* In general, bdiv may return either R = 0 or R = D when D divides
186      A. But R = 0 can happen only when A = 0, which we already have
187      excluded. Furthermore, R == D (mod B^{dn}) implies no carry, so
188      we don't need to check the carry returned from bdiv. */
189 
190   MPN_CMP (c, rp, dp, dn);
191 
192   TMP_FREE;
193   return c == 0;
194 }
195