xref: /netbsd-src/external/lgpl3/gmp/dist/mpz/aorsmul.c (revision 72c7faa4dbb41dbb0238d6b4a109da0d4b236dd4)
1 /* mpz_addmul, mpz_submul -- add or subtract multiple.
2 
3 Copyright 2001, 2004, 2005, 2012 Free Software Foundation, Inc.
4 
5 This file is part of the GNU MP Library.
6 
7 The GNU MP Library is free software; you can redistribute it and/or modify
8 it under the terms of either:
9 
10   * the GNU Lesser General Public License as published by the Free
11     Software Foundation; either version 3 of the License, or (at your
12     option) any later version.
13 
14 or
15 
16   * the GNU General Public License as published by the Free Software
17     Foundation; either version 2 of the License, or (at your option) any
18     later version.
19 
20 or both in parallel, as here.
21 
22 The GNU MP Library is distributed in the hope that it will be useful, but
23 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 for more details.
26 
27 You should have received copies of the GNU General Public License and the
28 GNU Lesser General Public License along with the GNU MP Library.  If not,
29 see https://www.gnu.org/licenses/.  */
30 
31 #include "gmp-impl.h"
32 
33 
34 /* expecting x and y both with non-zero high limbs */
35 #define mpn_cmp_twosizes_lt(xp,xsize, yp,ysize)                 \
36   ((xsize) < (ysize)                                            \
37    || ((xsize) == (ysize) && mpn_cmp (xp, yp, xsize) < 0))
38 
39 
40 /* sub>=0 means an addmul w += x*y, sub<0 means a submul w -= x*y.
41 
42    The signs of w, x and y are fully accounted for by each flipping "sub".
43 
44    The sign of w is retained for the result, unless the absolute value
45    submul underflows, in which case it flips.  */
46 
47 static void __gmpz_aorsmul (REGPARM_3_1 (mpz_ptr w, mpz_srcptr x, mpz_srcptr y, mp_size_t sub)) REGPARM_ATTR (1);
48 #define mpz_aorsmul(w,x,y,sub)  __gmpz_aorsmul (REGPARM_3_1 (w, x, y, sub))
49 
50 REGPARM_ATTR (1) static void
mpz_aorsmul(mpz_ptr w,mpz_srcptr x,mpz_srcptr y,mp_size_t sub)51 mpz_aorsmul (mpz_ptr w, mpz_srcptr x, mpz_srcptr y, mp_size_t sub)
52 {
53   mp_size_t  xsize, ysize, tsize, wsize, wsize_signed;
54   mp_ptr     wp, tp;
55   mp_limb_t  c, high;
56   TMP_DECL;
57 
58   /* w unaffected if x==0 or y==0 */
59   xsize = SIZ(x);
60   ysize = SIZ(y);
61   if (xsize == 0 || ysize == 0)
62     return;
63 
64   /* make x the bigger of the two */
65   if (ABS(ysize) > ABS(xsize))
66     {
67       MPZ_SRCPTR_SWAP (x, y);
68       MP_SIZE_T_SWAP (xsize, ysize);
69     }
70 
71   sub ^= ysize;
72   ysize = ABS(ysize);
73 
74   /* use mpn_addmul_1/mpn_submul_1 if possible */
75   if (ysize == 1)
76     {
77       mpz_aorsmul_1 (w, x, PTR(y)[0], sub);
78       return;
79     }
80 
81   sub ^= xsize;
82   xsize = ABS(xsize);
83 
84   wsize_signed = SIZ(w);
85   sub ^= wsize_signed;
86   wsize = ABS(wsize_signed);
87 
88   tsize = xsize + ysize;
89   wp = MPZ_REALLOC (w, MAX (wsize, tsize) + 1);
90 
91   if (wsize_signed == 0)
92     {
93       /* Nothing to add to, just set w=x*y.  No w==x or w==y overlap here,
94 	 since we know x,y!=0 but w==0.  */
95       high = mpn_mul (wp, PTR(x),xsize, PTR(y),ysize);
96       tsize -= (high == 0);
97       SIZ(w) = (sub >= 0 ? tsize : -tsize);
98       return;
99     }
100 
101   TMP_MARK;
102   tp = TMP_ALLOC_LIMBS (tsize);
103 
104   high = mpn_mul (tp, PTR(x),xsize, PTR(y),ysize);
105   tsize -= (high == 0);
106   ASSERT (tp[tsize-1] != 0);
107   if (sub >= 0)
108     {
109       mp_srcptr up    = wp;
110       mp_size_t usize = wsize;
111 
112       if (usize < tsize)
113 	{
114 	  up	= tp;
115 	  usize = tsize;
116 	  tp	= wp;
117 	  tsize = wsize;
118 
119 	  wsize = usize;
120 	}
121 
122       c = mpn_add (wp, up,usize, tp,tsize);
123       wp[wsize] = c;
124       wsize += (c != 0);
125     }
126   else
127     {
128       mp_srcptr up    = wp;
129       mp_size_t usize = wsize;
130 
131       if (mpn_cmp_twosizes_lt (up,usize, tp,tsize))
132 	{
133 	  up	= tp;
134 	  usize = tsize;
135 	  tp	= wp;
136 	  tsize = wsize;
137 
138 	  wsize = usize;
139 	  wsize_signed = -wsize_signed;
140 	}
141 
142       ASSERT_NOCARRY (mpn_sub (wp, up,usize, tp,tsize));
143       wsize = usize;
144       MPN_NORMALIZE (wp, wsize);
145     }
146 
147   SIZ(w) = (wsize_signed >= 0 ? wsize : -wsize);
148 
149   TMP_FREE;
150 }
151 
152 
153 void
mpz_addmul(mpz_ptr w,mpz_srcptr u,mpz_srcptr v)154 mpz_addmul (mpz_ptr w, mpz_srcptr u, mpz_srcptr v)
155 {
156   mpz_aorsmul (w, u, v, (mp_size_t) 0);
157 }
158 
159 void
mpz_submul(mpz_ptr w,mpz_srcptr u,mpz_srcptr v)160 mpz_submul (mpz_ptr w, mpz_srcptr u, mpz_srcptr v)
161 {
162   mpz_aorsmul (w, u, v, (mp_size_t) -1);
163 }
164