xref: /netbsd-src/external/lgpl3/gmp/dist/mpz/gcd.c (revision 6cd39ddb8550f6fa1bff3fed32053d7f19fd0453)
1 /* mpz/gcd.c:   Calculate the greatest common divisor of two integers.
2 
3 Copyright 1991, 1993, 1994, 1996, 2000, 2001, 2002, 2005, 2010 Free
4 Software Foundation, Inc.
5 
6 This file is part of the GNU MP Library.
7 
8 The GNU MP Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 The GNU MP Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16 License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public License
19 along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
20 
21 #include "gmp.h"
22 #include "gmp-impl.h"
23 #include "longlong.h"
24 
25 
26 void
27 mpz_gcd (mpz_ptr g, mpz_srcptr u, mpz_srcptr v)
28 {
29   unsigned long int g_zero_bits, u_zero_bits, v_zero_bits;
30   mp_size_t g_zero_limbs, u_zero_limbs, v_zero_limbs;
31   mp_ptr tp;
32   mp_ptr up;
33   mp_size_t usize;
34   mp_ptr vp;
35   mp_size_t vsize;
36   mp_size_t gsize;
37   TMP_DECL;
38 
39   up = PTR(u);
40   usize = ABSIZ (u);
41   vp = PTR(v);
42   vsize = ABSIZ (v);
43   /* GCD(0, V) == V.  */
44   if (usize == 0)
45     {
46       SIZ (g) = vsize;
47       if (g == v)
48 	return;
49       MPZ_REALLOC (g, vsize);
50       MPN_COPY (PTR (g), vp, vsize);
51       return;
52     }
53 
54   /* GCD(U, 0) == U.  */
55   if (vsize == 0)
56     {
57       SIZ (g) = usize;
58       if (g == u)
59 	return;
60       MPZ_REALLOC (g, usize);
61       MPN_COPY (PTR (g), up, usize);
62       return;
63     }
64 
65   if (usize == 1)
66     {
67       SIZ (g) = 1;
68       PTR (g)[0] = mpn_gcd_1 (vp, vsize, up[0]);
69       return;
70     }
71 
72   if (vsize == 1)
73     {
74       SIZ(g) = 1;
75       PTR (g)[0] = mpn_gcd_1 (up, usize, vp[0]);
76       return;
77     }
78 
79   TMP_MARK;
80 
81   /*  Eliminate low zero bits from U and V and move to temporary storage.  */
82   while (*up == 0)
83     up++;
84   u_zero_limbs = up - PTR(u);
85   usize -= u_zero_limbs;
86   count_trailing_zeros (u_zero_bits, *up);
87   tp = up;
88   up = TMP_ALLOC_LIMBS (usize);
89   if (u_zero_bits != 0)
90     {
91       mpn_rshift (up, tp, usize, u_zero_bits);
92       usize -= up[usize - 1] == 0;
93     }
94   else
95     MPN_COPY (up, tp, usize);
96 
97   while (*vp == 0)
98     vp++;
99   v_zero_limbs = vp - PTR (v);
100   vsize -= v_zero_limbs;
101   count_trailing_zeros (v_zero_bits, *vp);
102   tp = vp;
103   vp = TMP_ALLOC_LIMBS (vsize);
104   if (v_zero_bits != 0)
105     {
106       mpn_rshift (vp, tp, vsize, v_zero_bits);
107       vsize -= vp[vsize - 1] == 0;
108     }
109   else
110     MPN_COPY (vp, tp, vsize);
111 
112   if (u_zero_limbs > v_zero_limbs)
113     {
114       g_zero_limbs = v_zero_limbs;
115       g_zero_bits = v_zero_bits;
116     }
117   else if (u_zero_limbs < v_zero_limbs)
118     {
119       g_zero_limbs = u_zero_limbs;
120       g_zero_bits = u_zero_bits;
121     }
122   else  /*  Equal.  */
123     {
124       g_zero_limbs = u_zero_limbs;
125       g_zero_bits = MIN (u_zero_bits, v_zero_bits);
126     }
127 
128   /*  Call mpn_gcd.  The 2nd argument must not have more bits than the 1st.  */
129   vsize = (usize < vsize || (usize == vsize && up[usize-1] < vp[vsize-1]))
130     ? mpn_gcd (vp, vp, vsize, up, usize)
131     : mpn_gcd (vp, up, usize, vp, vsize);
132 
133   /*  Here G <-- V << (g_zero_limbs*GMP_LIMB_BITS + g_zero_bits).  */
134   gsize = vsize + g_zero_limbs;
135   if (g_zero_bits != 0)
136     {
137       mp_limb_t cy_limb;
138       gsize += (vp[vsize - 1] >> (GMP_NUMB_BITS - g_zero_bits)) != 0;
139       MPZ_REALLOC (g, gsize);
140       MPN_ZERO (PTR (g), g_zero_limbs);
141 
142       tp = PTR(g) + g_zero_limbs;
143       cy_limb = mpn_lshift (tp, vp, vsize, g_zero_bits);
144       if (cy_limb != 0)
145 	tp[vsize] = cy_limb;
146     }
147   else
148     {
149       MPZ_REALLOC (g, gsize);
150       MPN_ZERO (PTR (g), g_zero_limbs);
151       MPN_COPY (PTR (g) + g_zero_limbs, vp, vsize);
152     }
153 
154   SIZ (g) = gsize;
155   TMP_FREE;
156 }
157