Line | Count | Source (jump to first uncovered line) |
1 | | /* mpz_addmul_ui, mpz_submul_ui - add or subtract small multiple. |
2 | | |
3 | | THE mpz_aorsmul_1 FUNCTION IN THIS FILE IS FOR INTERNAL USE ONLY AND IS |
4 | | ALMOST CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR |
5 | | COMPLETELY IN FUTURE GNU MP RELEASES. |
6 | | |
7 | | Copyright 2001, 2002, 2004, 2005, 2012, 2021, 2022 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 | | |
38 | | |
39 | | #if HAVE_NATIVE_mpn_mul_1c |
40 | | #define MPN_MUL_1C(cout, dst, src, size, n, cin) \ |
41 | 0 | do { \ |
42 | 0 | (cout) = mpn_mul_1c (dst, src, size, n, cin); \ |
43 | 0 | } while (0) |
44 | | #else |
45 | | #define MPN_MUL_1C(cout, dst, src, size, n, cin) \ |
46 | | do { \ |
47 | | mp_limb_t __cy; \ |
48 | | __cy = mpn_mul_1 (dst, src, size, n); \ |
49 | | (cout) = __cy + mpn_add_1 (dst, dst, size, cin); \ |
50 | | } while (0) |
51 | | #endif |
52 | | |
53 | | |
54 | | /* sub>=0 means an addmul w += x*y, sub<0 means a submul w -= x*y. |
55 | | |
56 | | All that's needed to account for negative w or x is to flip "sub". |
57 | | |
58 | | The final w will retain its sign, unless an underflow occurs in a submul |
59 | | of absolute values, in which case it's flipped. |
60 | | |
61 | | If x has more limbs than w, then mpn_submul_1 followed by mpn_com is |
62 | | used. The alternative would be mpn_mul_1 into temporary space followed |
63 | | by mpn_sub_n. Avoiding temporary space seem good, and submul+com stands |
64 | | a chance of being faster since it involves only one set of carry |
65 | | propagations, not two. Note that doing an addmul_1 with a |
66 | | twos-complement negative y doesn't work, because it effectively adds an |
67 | | extra x * 2^GMP_LIMB_BITS. */ |
68 | | |
69 | | REGPARM_ATTR(1) void |
70 | | mpz_aorsmul_1 (mpz_ptr w, mpz_srcptr x, mp_limb_t y, mp_size_t sub) |
71 | 0 | { |
72 | 0 | mp_size_t xsize, wsize, wsize_signed, new_wsize, min_size, dsize; |
73 | 0 | mp_srcptr xp; |
74 | 0 | mp_ptr wp; |
75 | 0 | mp_limb_t cy; |
76 | | |
77 | | /* w unaffected if x==0 or y==0 */ |
78 | 0 | xsize = SIZ (x); |
79 | 0 | if (xsize == 0 || y == 0) |
80 | 0 | return; |
81 | | |
82 | 0 | sub ^= xsize; |
83 | 0 | xsize = ABS (xsize); |
84 | |
|
85 | 0 | wsize_signed = SIZ (w); |
86 | 0 | if (wsize_signed == 0) |
87 | 0 | { |
88 | | /* nothing to add to, just set x*y, "sub" gives the sign */ |
89 | 0 | wp = MPZ_NEWALLOC (w, xsize+1); |
90 | 0 | cy = mpn_mul_1 (wp, PTR(x), xsize, y); |
91 | 0 | wp[xsize] = cy; |
92 | 0 | xsize += (cy != 0); |
93 | 0 | SIZ (w) = (sub >= 0 ? xsize : -xsize); |
94 | 0 | return; |
95 | 0 | } |
96 | | |
97 | 0 | sub ^= wsize_signed; |
98 | 0 | wsize = ABS (wsize_signed); |
99 | |
|
100 | 0 | new_wsize = MAX (wsize, xsize); |
101 | 0 | wp = MPZ_REALLOC (w, new_wsize+1); |
102 | 0 | xp = PTR (x); |
103 | 0 | min_size = MIN (wsize, xsize); |
104 | |
|
105 | 0 | if (sub >= 0) |
106 | 0 | { |
107 | | /* addmul of absolute values */ |
108 | |
|
109 | 0 | cy = mpn_addmul_1 (wp, xp, min_size, y); |
110 | 0 | wp += min_size; |
111 | 0 | xp += min_size; |
112 | |
|
113 | 0 | dsize = xsize - wsize; |
114 | 0 | #if HAVE_NATIVE_mpn_mul_1c |
115 | 0 | if (dsize > 0) |
116 | 0 | cy = mpn_mul_1c (wp, xp, dsize, y, cy); |
117 | 0 | else if (dsize < 0) |
118 | 0 | { |
119 | 0 | dsize = -dsize; |
120 | 0 | cy = mpn_add_1 (wp, wp, dsize, cy); |
121 | 0 | } |
122 | | #else |
123 | | if (dsize != 0) |
124 | | { |
125 | | mp_limb_t cy2; |
126 | | if (dsize > 0) |
127 | | cy2 = mpn_mul_1 (wp, xp, dsize, y); |
128 | | else |
129 | | { |
130 | | dsize = -dsize; |
131 | | cy2 = 0; |
132 | | } |
133 | | cy = cy2 + mpn_add_1 (wp, wp, dsize, cy); |
134 | | } |
135 | | #endif |
136 | |
|
137 | 0 | wp[dsize] = cy; |
138 | 0 | new_wsize += (cy != 0); |
139 | 0 | } |
140 | 0 | else |
141 | 0 | { |
142 | | /* submul of absolute values */ |
143 | |
|
144 | 0 | cy = mpn_submul_1 (wp, xp, min_size, y); |
145 | 0 | if (wsize >= xsize) |
146 | 0 | { |
147 | | /* if w bigger than x, then propagate borrow through it */ |
148 | 0 | if (wsize != xsize) |
149 | 0 | cy = mpn_sub_1 (wp+xsize, wp+xsize, wsize-xsize, cy); |
150 | |
|
151 | 0 | if (cy != 0) |
152 | 0 | { |
153 | | /* Borrow out of w, take twos complement negative to get |
154 | | absolute value, flip sign of w. */ |
155 | 0 | cy -= mpn_neg (wp, wp, new_wsize); |
156 | 0 | wp[new_wsize] = cy; |
157 | 0 | new_wsize += (cy != 0); |
158 | 0 | wsize_signed = -wsize_signed; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | else /* wsize < xsize */ |
162 | 0 | { |
163 | | /* x bigger than w, so want x*y-w. Submul has given w-x*y, so |
164 | | take twos complement and use an mpn_mul_1 for the rest. */ |
165 | |
|
166 | 0 | mp_limb_t cy2; |
167 | | |
168 | | /* -(-cy*b^n + w-x*y) = (cy-1)*b^n + ~(w-x*y) + 1 */ |
169 | 0 | cy -= mpn_neg (wp, wp, wsize); |
170 | | |
171 | | /* If cy-1 == -1 then hold that -1 for latter. mpn_submul_1 never |
172 | | returns cy==MP_LIMB_T_MAX so that value always indicates a -1. */ |
173 | 0 | cy2 = (cy == MP_LIMB_T_MAX); |
174 | 0 | cy += cy2; |
175 | 0 | MPN_MUL_1C (cy, wp+wsize, xp+wsize, xsize-wsize, y, cy); |
176 | 0 | wp[new_wsize] = cy; |
177 | 0 | new_wsize += (cy != 0); |
178 | | |
179 | | /* Apply any -1 from above. The value at wp+wsize is non-zero |
180 | | because y!=0 and the high limb of x will be non-zero. */ |
181 | 0 | if (cy2) |
182 | 0 | MPN_DECR_U (wp+wsize, new_wsize-wsize, CNST_LIMB(1)); |
183 | |
|
184 | 0 | wsize_signed = -wsize_signed; |
185 | 0 | } |
186 | | |
187 | | /* submul can produce high zero limbs due to cancellation, both when w |
188 | | has more limbs or x has more */ |
189 | 0 | MPN_NORMALIZE (wp, new_wsize); |
190 | 0 | } |
191 | |
|
192 | 0 | SIZ (w) = (wsize_signed >= 0 ? new_wsize : -new_wsize); |
193 | |
|
194 | 0 | ASSERT (new_wsize == 0 || PTR(w)[new_wsize-1] != 0); |
195 | 0 | } |
196 | | |
197 | | |
198 | | void |
199 | | mpz_addmul_ui (mpz_ptr w, mpz_srcptr x, unsigned long y) |
200 | 0 | { |
201 | | #if BITS_PER_ULONG > GMP_NUMB_BITS |
202 | | if (UNLIKELY (y > GMP_NUMB_MAX)) |
203 | | { |
204 | | mpz_t t; |
205 | | mp_ptr tp; |
206 | | mp_size_t xn; |
207 | | TMP_DECL; |
208 | | TMP_MARK; |
209 | | xn = SIZ (x); |
210 | | if (xn == 0) return; |
211 | | MPZ_TMP_INIT (t, ABS (xn) + 1); |
212 | | tp = PTR (t); |
213 | | tp[0] = 0; |
214 | | MPN_COPY (tp + 1, PTR(x), ABS (xn)); |
215 | | SIZ(t) = xn >= 0 ? xn + 1 : xn - 1; |
216 | | mpz_aorsmul_1 (w, t, (mp_limb_t) y >> GMP_NUMB_BITS, (mp_size_t) 0); |
217 | | PTR(t) = tp + 1; |
218 | | SIZ(t) = xn; |
219 | | mpz_aorsmul_1 (w, t, (mp_limb_t) y & GMP_NUMB_MASK, (mp_size_t) 0); |
220 | | TMP_FREE; |
221 | | return; |
222 | | } |
223 | | #endif |
224 | 0 | mpz_aorsmul_1 (w, x, (mp_limb_t) y, (mp_size_t) 0); |
225 | 0 | } |
226 | | |
227 | | void |
228 | | mpz_submul_ui (mpz_ptr w, mpz_srcptr x, unsigned long y) |
229 | 0 | { |
230 | | #if BITS_PER_ULONG > GMP_NUMB_BITS |
231 | | if (y > GMP_NUMB_MAX) |
232 | | { |
233 | | mpz_t t; |
234 | | mp_ptr tp; |
235 | | mp_size_t xn; |
236 | | TMP_DECL; |
237 | | TMP_MARK; |
238 | | xn = SIZ (x); |
239 | | if (xn == 0) return; |
240 | | MPZ_TMP_INIT (t, ABS (xn) + 1); |
241 | | tp = PTR (t); |
242 | | tp[0] = 0; |
243 | | MPN_COPY (tp + 1, PTR(x), ABS (xn)); |
244 | | SIZ(t) = xn >= 0 ? xn + 1 : xn - 1; |
245 | | mpz_aorsmul_1 (w, t, (mp_limb_t) y >> GMP_NUMB_BITS, (mp_size_t) -1); |
246 | | PTR(t) = tp + 1; |
247 | | SIZ(t) = xn; |
248 | | mpz_aorsmul_1 (w, t, (mp_limb_t) y & GMP_NUMB_MASK, (mp_size_t) -1); |
249 | | TMP_FREE; |
250 | | return; |
251 | | } |
252 | | #endif |
253 | 0 | mpz_aorsmul_1 (w, x, (mp_limb_t) y & GMP_NUMB_MASK, (mp_size_t) -1); |
254 | 0 | } |