1 /* mpn_toom42_mulmid -- toom42 middle product
3 Contributed by David Harvey.
5 THE FUNCTION IN THIS FILE IS INTERNAL WITH A MUTABLE INTERFACE. IT IS ONLY
6 SAFE TO REACH IT THROUGH DOCUMENTED INTERFACES. IN FACT, IT IS ALMOST
7 GUARANTEED THAT IT'LL CHANGE OR DISAPPEAR IN A FUTURE GNU MP RELEASE.
9 Copyright 2011 Free Software Foundation, Inc.
11 This file is part of the GNU MP Library.
13 The GNU MP Library is free software; you can redistribute it and/or modify
14 it under the terms of either:
16 * the GNU Lesser General Public License as published by the Free
17 Software Foundation; either version 3 of the License, or (at your
18 option) any later version.
22 * the GNU General Public License as published by the Free Software
23 Foundation; either version 2 of the License, or (at your option) any
26 or both in parallel, as here.
28 The GNU MP Library is distributed in the hope that it will be useful, but
29 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
30 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
33 You should have received copies of the GNU General Public License and the
34 GNU Lesser General Public License along with the GNU MP Library. If not,
35 see https://www.gnu.org/licenses/. */
44 Middle product of {ap,2n-1} and {bp,n}, output written to {rp,n+2}.
46 Neither ap nor bp may overlap rp.
50 Amount of scratch space required is given by mpn_toom42_mulmid_itch().
52 FIXME: this code assumes that n is small compared to GMP_NUMB_MAX. The exact
53 requirements should be clarified.
56 mpn_toom42_mulmid (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n,
59 mp_limb_t cy, e[12], zh, zl;
64 ASSERT (! MPN_OVERLAP_P (rp, n + 2, ap, 2*n - 1));
65 ASSERT (! MPN_OVERLAP_P (rp, n + 2, bp, n));
67 ap += n & 1; /* handle odd row and diagonal later */
70 /* (e0h:e0l) etc are correction terms, in 2's complement */
84 #define s (scratch + 2)
85 #define t (rp + m + 2)
89 #define next_scratch (scratch + 3*m + 1)
93 |---------|-----------| |---------|---------|----------|
95 <----p2----> <-------------s------------->
96 <----p0----><---t----> <----p1---->
99 /* compute {s,3m-1} = {a,3m-1} + {a+m,3m-1} and error terms e0, e1, e2, e3 */
100 cy = mpn_add_err1_n (s, ap, ap + m, &e0l, bp + m, m - 1, 0);
101 cy = mpn_add_err2_n (s + m - 1, ap + m - 1, ap + 2*m - 1, &e1l,
103 mpn_add_err1_n (s + 2*m - 1, ap + 2*m - 1, ap + 3*m - 1, &e3l, bp, m, cy);
105 /* compute t = (-1)^neg * ({b,m} - {b+m,m}) and error terms e4, e5 */
106 if (mpn_cmp (bp + m, bp, m) < 0)
108 ASSERT_NOCARRY (mpn_sub_err2_n (t, bp, bp + m, &e4l,
109 ap + m - 1, ap + 2*m - 1, m, 0));
114 ASSERT_NOCARRY (mpn_sub_err2_n (t, bp + m, bp, &e4l,
115 ap + m - 1, ap + 2*m - 1, m, 0));
119 /* recursive middle products. The picture is:
121 b[2m-1] A A A B B B - - - - -
122 ... - A A A B B B - - - -
123 b[m] - - A A A B B B - - -
124 b[m-1] - - - C C C D D D - -
125 ... - - - - C C C D D D -
126 b[0] - - - - - C C C D D D
127 a[0] ... a[m] ... a[2m] ... a[4m-2]
130 if (m < MULMID_TOOM42_THRESHOLD)
133 mpn_mulmid_basecase (p0, s, 2*m - 1, bp + m, m);
134 /* accumulate high limbs of p0 into e1 */
135 ADDC_LIMB (cy, e1l, e1l, p0[m]);
136 e1h += p0[m + 1] + cy;
137 /* (-1)^neg * (B - C) (overwrites first m limbs of s) */
138 mpn_mulmid_basecase (p1, ap + m, 2*m - 1, t, m);
139 /* C + D (overwrites t) */
140 mpn_mulmid_basecase (p2, s + m, 2*m - 1, bp, m);
144 /* as above, but use toom42 instead */
145 mpn_toom42_mulmid (p0, s, bp + m, m, next_scratch);
146 ADDC_LIMB (cy, e1l, e1l, p0[m]);
147 e1h += p0[m + 1] + cy;
148 mpn_toom42_mulmid (p1, ap + m, t, m, next_scratch);
149 mpn_toom42_mulmid (p2, s + m, bp, m, next_scratch);
152 /* apply error terms */
155 SUBC_LIMB (cy, rp[0], rp[0], e0l);
156 SUBC_LIMB (cy, rp[1], rp[1], e0h + cy);
159 cy = (m > 2) ? mpn_sub_1 (rp + 2, rp + 2, m - 2, 1) : 1;
160 SUBC_LIMB (cy, e1l, e1l, cy);
164 /* z = e1 - e2 + high(p0) */
165 SUBC_LIMB (cy, zl, e1l, e2l);
169 ADDC_LIMB (cy, rp[m], rp[m], zl);
170 zh = (zh + cy) & GMP_NUMB_MASK;
171 ADDC_LIMB (cy, rp[m + 1], rp[m + 1], zh);
172 cy -= (zh >> (GMP_NUMB_BITS - 1));
176 mpn_add_1 (rp + m + 2, rp + m + 2, m, 1);
178 mpn_sub_1 (rp + m + 2, rp + m + 2, m, 1);
182 ADDC_LIMB (cy, rp[2*m], rp[2*m], e3l);
183 rp[2*m + 1] = (rp[2*m + 1] + e3h + cy) & GMP_NUMB_MASK;
186 ADDC_LIMB (cy, p1[0], p1[0], e4l);
187 ADDC_LIMB (cy, p1[1], p1[1], e4h + cy);
189 mpn_add_1 (p1 + 2, p1 + 2, m, 1);
192 SUBC_LIMB (cy, p1[m], p1[m], e5l);
193 p1[m + 1] = (p1[m + 1] - e5h - cy) & GMP_NUMB_MASK;
195 /* adjustment if p1 ends up negative */
196 cy = (p1[m + 1] >> (GMP_NUMB_BITS - 1));
198 /* add (-1)^neg * (p1 - B^m * p1) to output */
201 mpn_sub_1 (rp + m + 2, rp + m + 2, m, cy);
202 mpn_add (rp, rp, 2*m + 2, p1, m + 2); /* A + C */
203 mpn_sub_n (rp + m, rp + m, p1, m + 2); /* B + D */
207 mpn_add_1 (rp + m + 2, rp + m + 2, m, cy);
208 mpn_sub (rp, rp, 2*m + 2, p1, m + 2); /* A + C */
209 mpn_add_n (rp + m, rp + m, p1, m + 2); /* B + D */
212 /* odd row and diagonal */
216 Products marked E are already done. We need to do products marked O.
225 /* first row of O's */
226 cy = mpn_addmul_1 (rp, ap - 1, n, bp[n - 1]);
227 ADDC_LIMB (rp[n + 1], rp[n], rp[n], cy);
229 /* O's on diagonal */
230 /* FIXME: should probably define an interface "mpn_mulmid_diag_1"
231 that can handle the sum below. Currently we're relying on
232 mulmid_basecase being pretty fast for a diagonal sum like this,
233 which is true at least for the K8 asm version, but surely false
234 for the generic version. */
235 mpn_mulmid_basecase (e, ap + n - 1, n - 1, bp, n - 1);
236 mpn_add_n (rp + n - 1, rp + n - 1, e, 3);