1/* mpz_add, mpz_sub -- add or subtract integers.
2
3Copyright 1991, 1993, 1994, 1996, 2000, 2001 Free Software Foundation, Inc.
4
5This file is part of the GNU MP Library.
6
7The GNU MP Library is free software; you can redistribute it and/or modify
8it under the terms of the GNU Lesser General Public License as published by
9the Free Software Foundation; either version 3 of the License, or (at your
10option) any later version.
11
12The GNU MP Library is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15License for more details.
16
17You should have received a copy of the GNU Lesser General Public License
18along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
19
20#include "gmp.h"
21#include "gmp-impl.h"
22
23
24#ifdef BERKELEY_MP
25
26#include "mp.h"
27#ifdef OPERATION_add
28#define FUNCTION     madd
29#define VARIATION
30#endif
31#ifdef OPERATION_sub
32#define FUNCTION     msub
33#define VARIATION    -
34#endif
35#define ARGUMENTS    mpz_srcptr u, mpz_srcptr v, mpz_ptr w
36
37#else /* normal GMP */
38
39#ifdef OPERATION_add
40#define FUNCTION     mpz_add
41#define VARIATION
42#endif
43#ifdef OPERATION_sub
44#define FUNCTION     mpz_sub
45#define VARIATION    -
46#endif
47#define ARGUMENTS    mpz_ptr w, mpz_srcptr u, mpz_srcptr v
48
49#endif
50
51#ifndef FUNCTION
52Error, need OPERATION_add or OPERATION_sub
53#endif
54
55
56void
57FUNCTION (ARGUMENTS)
58{
59  mp_srcptr up, vp;
60  mp_ptr wp;
61  mp_size_t usize, vsize, wsize;
62  mp_size_t abs_usize;
63  mp_size_t abs_vsize;
64
65  usize = u->_mp_size;
66  vsize = VARIATION v->_mp_size;
67  abs_usize = ABS (usize);
68  abs_vsize = ABS (vsize);
69
70  if (abs_usize < abs_vsize)
71    {
72      /* Swap U and V. */
73      MPZ_SRCPTR_SWAP (u, v);
74      MP_SIZE_T_SWAP (usize, vsize);
75      MP_SIZE_T_SWAP (abs_usize, abs_vsize);
76    }
77
78  /* True: ABS_USIZE >= ABS_VSIZE.  */
79
80  /* If not space for w (and possible carry), increase space.  */
81  wsize = abs_usize + 1;
82  if (w->_mp_alloc < wsize)
83    _mpz_realloc (w, wsize);
84
85  /* These must be after realloc (u or v may be the same as w).  */
86  up = u->_mp_d;
87  vp = v->_mp_d;
88  wp = w->_mp_d;
89
90  if ((usize ^ vsize) < 0)
91    {
92      /* U and V have different sign.  Need to compare them to determine
93	 which operand to subtract from which.  */
94
95      /* This test is right since ABS_USIZE >= ABS_VSIZE.  */
96      if (abs_usize != abs_vsize)
97	{
98	  mpn_sub (wp, up, abs_usize, vp, abs_vsize);
99	  wsize = abs_usize;
100	  MPN_NORMALIZE (wp, wsize);
101	  if (usize < 0)
102	    wsize = -wsize;
103	}
104      else if (mpn_cmp (up, vp, abs_usize) < 0)
105	{
106	  mpn_sub_n (wp, vp, up, abs_usize);
107	  wsize = abs_usize;
108	  MPN_NORMALIZE (wp, wsize);
109	  if (usize >= 0)
110	    wsize = -wsize;
111	}
112      else
113	{
114	  mpn_sub_n (wp, up, vp, abs_usize);
115	  wsize = abs_usize;
116	  MPN_NORMALIZE (wp, wsize);
117	  if (usize < 0)
118	    wsize = -wsize;
119	}
120    }
121  else
122    {
123      /* U and V have same sign.  Add them.  */
124      mp_limb_t cy_limb = mpn_add (wp, up, abs_usize, vp, abs_vsize);
125      wp[abs_usize] = cy_limb;
126      wsize = abs_usize + cy_limb;
127      if (usize < 0)
128	wsize = -wsize;
129    }
130
131  w->_mp_size = wsize;
132}
133