1 /* mpn_dcpi1_div_qr_n -- recursive divide-and-conquer division for arbitrary
2    size operands.
3 
4    Contributed to the GNU project by Torbjorn Granlund.
5 
6    THE FUNCTIONS IN THIS FILE ARE INTERNAL WITH MUTABLE INTERFACES.  IT IS ONLY
7    SAFE TO REACH THEM THROUGH DOCUMENTED INTERFACES.  IN FACT, IT IS ALMOST
8    GUARANTEED THAT THEY WILL CHANGE OR DISAPPEAR IN A FUTURE GMP RELEASE.
9 
10 Copyright 2006, 2007, 2009 Free Software Foundation, Inc.
11 
12 This file is part of the GNU MP Library.
13 
14 The GNU MP Library is free software; you can redistribute it and/or modify
15 it under the terms of either:
16 
17   * the GNU Lesser General Public License as published by the Free
18     Software Foundation; either version 3 of the License, or (at your
19     option) any later version.
20 
21 or
22 
23   * the GNU General Public License as published by the Free Software
24     Foundation; either version 2 of the License, or (at your option) any
25     later version.
26 
27 or both in parallel, as here.
28 
29 The GNU MP Library is distributed in the hope that it will be useful, but
30 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
31 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
32 for more details.
33 
34 You should have received copies of the GNU General Public License and the
35 GNU Lesser General Public License along with the GNU MP Library.  If not,
36 see https://www.gnu.org/licenses/.  */
37 
38 #include "gmp-impl.h"
39 #include "longlong.h"
40 
41 
42 mp_limb_t
mpn_dcpi1_div_qr_n(mp_ptr qp,mp_ptr np,mp_srcptr dp,mp_size_t n,gmp_pi1_t * dinv,mp_ptr tp)43 mpn_dcpi1_div_qr_n (mp_ptr qp, mp_ptr np, mp_srcptr dp, mp_size_t n,
44                         gmp_pi1_t *dinv, mp_ptr tp)
45 {
46   mp_size_t lo, hi;
47   mp_limb_t cy, qh, ql;
48 
49   lo = n >> 1;                          /* floor(n/2) */
50   hi = n - lo;                          /* ceil(n/2) */
51 
52   if (BELOW_THRESHOLD (hi, DC_DIV_QR_THRESHOLD))
53     qh = mpn_sbpi1_div_qr (qp + lo, np + 2 * lo, 2 * hi, dp + lo, hi, dinv->inv32);
54   else
55     qh = mpn_dcpi1_div_qr_n (qp + lo, np + 2 * lo, dp + lo, hi, dinv, tp);
56 
57   mpn_mul (tp, qp + lo, hi, dp, lo);
58 
59   cy = mpn_sub_n (np + lo, np + lo, tp, n);
60   if (qh != 0)
61     cy += mpn_sub_n (np + n, np + n, dp, lo);
62 
63   while (cy != 0)
64     {
65       qh -= mpn_sub_1 (qp + lo, qp + lo, hi, 1);
66       cy -= mpn_add_n (np + lo, np + lo, dp, n);
67     }
68 
69   if (BELOW_THRESHOLD (lo, DC_DIV_QR_THRESHOLD))
70     ql = mpn_sbpi1_div_qr (qp, np + hi, 2 * lo, dp + hi, lo, dinv->inv32);
71   else
72     ql = mpn_dcpi1_div_qr_n (qp, np + hi, dp + hi, lo, dinv, tp);
73 
74   mpn_mul (tp, dp, hi, qp, lo);
75 
76   cy = mpn_sub_n (np, np, tp, n);
77   if (ql != 0)
78     cy += mpn_sub_n (np + lo, np + lo, dp, hi);
79 
80   while (cy != 0)
81     {
82       mpn_sub_1 (qp, qp, lo, 1);
83       cy -= mpn_add_n (np, np, dp, n);
84     }
85 
86   return qh;
87 }
88 
89 mp_limb_t
mpn_dcpi1_div_qr(mp_ptr qp,mp_ptr np,mp_size_t nn,mp_srcptr dp,mp_size_t dn,gmp_pi1_t * dinv)90 mpn_dcpi1_div_qr (mp_ptr qp,
91                       mp_ptr np, mp_size_t nn,
92                       mp_srcptr dp, mp_size_t dn,
93                       gmp_pi1_t *dinv)
94 {
95   mp_size_t qn;
96   mp_limb_t qh, cy;
97   mp_ptr tp;
98   TMP_DECL;
99 
100   TMP_MARK;
101 
102   ASSERT (dn >= 6);           /* to adhere to mpn_sbpi1_div_qr's limits */
103   ASSERT (nn - dn >= 3);      /* to adhere to mpn_sbpi1_div_qr's limits */
104   ASSERT (dp[dn-1] & GMP_NUMB_HIGHBIT);
105 
106   tp = TMP_ALLOC_LIMBS (dn);
107 
108   qn = nn - dn;
109   qp += qn;
110   np += nn;
111   dp += dn;
112 
113   if (qn > dn)
114     {
115       /* Reduce qn mod dn without division, optimizing small operations.  */
116       do
117           qn -= dn;
118       while (qn > dn);
119 
120       qp -= qn;                         /* point at low limb of next quotient block */
121       np -= qn;                         /* point in the middle of partial remainder */
122 
123       /* Perform the typically smaller block first.  */
124       if (qn == 1)
125           {
126             mp_limb_t q, n2, n1, n0, d1, d0;
127 
128             /* Handle qh up front, for simplicity. */
129             qh = mpn_cmp (np - dn + 1, dp - dn, dn) >= 0;
130             if (qh)
131               ASSERT_NOCARRY (mpn_sub_n (np - dn + 1, np - dn + 1, dp - dn, dn));
132 
133             /* A single iteration of schoolbook: One 3/2 division,
134                followed by the bignum update and adjustment. */
135             n2 = np[0];
136             n1 = np[-1];
137             n0 = np[-2];
138             d1 = dp[-1];
139             d0 = dp[-2];
140 
141             ASSERT (n2 < d1 || (n2 == d1 && n1 <= d0));
142 
143             if (UNLIKELY (n2 == d1) && n1 == d0)
144               {
145                 q = GMP_NUMB_MASK;
146                 cy = mpn_submul_1 (np - dn, dp - dn, dn, q);
147                 ASSERT (cy == n2);
148               }
149             else
150               {
151                 udiv_qr_3by2 (q, n1, n0, n2, n1, n0, d1, d0, dinv->inv32);
152 
153                 if (dn > 2)
154                     {
155                       mp_limb_t cy, cy1;
156                       cy = mpn_submul_1 (np - dn, dp - dn, dn - 2, q);
157 
158                       cy1 = n0 < cy;
159                       n0 = (n0 - cy) & GMP_NUMB_MASK;
160                       cy = n1 < cy1;
161                       n1 = (n1 - cy1) & GMP_NUMB_MASK;
162                       np[-2] = n0;
163 
164                       if (UNLIKELY (cy != 0))
165                         {
166                           n1 += d1 + mpn_add_n (np - dn, np - dn, dp - dn, dn - 1);
167                           qh -= (q == 0);
168                           q = (q - 1) & GMP_NUMB_MASK;
169                         }
170                     }
171                 else
172                     np[-2] = n0;
173 
174                 np[-1] = n1;
175               }
176             qp[0] = q;
177           }
178       else
179           {
180             /* Do a 2qn / qn division */
181             if (qn == 2)
182               qh = mpn_divrem_2 (qp, 0L, np - 2, 4, dp - 2); /* FIXME: obsolete function. Use 5/3 division? */
183             else if (BELOW_THRESHOLD (qn, DC_DIV_QR_THRESHOLD))
184               qh = mpn_sbpi1_div_qr (qp, np - qn, 2 * qn, dp - qn, qn, dinv->inv32);
185             else
186               qh = mpn_dcpi1_div_qr_n (qp, np - qn, dp - qn, qn, dinv, tp);
187 
188             if (qn != dn)
189               {
190                 if (qn > dn - qn)
191                     mpn_mul (tp, qp, qn, dp - dn, dn - qn);
192                 else
193                     mpn_mul (tp, dp - dn, dn - qn, qp, qn);
194 
195                 cy = mpn_sub_n (np - dn, np - dn, tp, dn);
196                 if (qh != 0)
197                     cy += mpn_sub_n (np - dn + qn, np - dn + qn, dp - dn, dn - qn);
198 
199                 while (cy != 0)
200                     {
201                       qh -= mpn_sub_1 (qp, qp, qn, 1);
202                       cy -= mpn_add_n (np - dn, np - dn, dp - dn, dn);
203                     }
204               }
205           }
206 
207       qn = nn - dn - qn;
208       do
209           {
210             qp -= dn;
211             np -= dn;
212             mpn_dcpi1_div_qr_n (qp, np - dn, dp - dn, dn, dinv, tp);
213             qn -= dn;
214           }
215       while (qn > 0);
216     }
217   else
218     {
219       qp -= qn;                         /* point at low limb of next quotient block */
220       np -= qn;                         /* point in the middle of partial remainder */
221 
222       if (BELOW_THRESHOLD (qn, DC_DIV_QR_THRESHOLD))
223           qh = mpn_sbpi1_div_qr (qp, np - qn, 2 * qn, dp - qn, qn, dinv->inv32);
224       else
225           qh = mpn_dcpi1_div_qr_n (qp, np - qn, dp - qn, qn, dinv, tp);
226 
227       if (qn != dn)
228           {
229             if (qn > dn - qn)
230               mpn_mul (tp, qp, qn, dp - dn, dn - qn);
231             else
232               mpn_mul (tp, dp - dn, dn - qn, qp, qn);
233 
234             cy = mpn_sub_n (np - dn, np - dn, tp, dn);
235             if (qh != 0)
236               cy += mpn_sub_n (np - dn + qn, np - dn + qn, dp - dn, dn - qn);
237 
238             while (cy != 0)
239               {
240                 qh -= mpn_sub_1 (qp, qp, qn, 1);
241                 cy -= mpn_add_n (np - dn, np - dn, dp - dn, dn);
242               }
243           }
244     }
245 
246   TMP_FREE;
247   return qh;
248 }
249