GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
gcd.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2004-2025 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include "dNDArray.h"
31#include "CNDArray.h"
32#include "fNDArray.h"
33#include "fCNDArray.h"
34#include "lo-mappers.h"
35#include "oct-binmap.h"
36
37#include "defun.h"
38#include "error.h"
39#include "ovl.h"
40
42
43static double
44simple_gcd (double a, double b)
45{
46 if (! math::isinteger (a) || ! math::isinteger (b))
47 error ("gcd: all values must be integers");
48
49 double aa = fabs (a);
50 double bb = fabs (b);
51
52 while (bb != 0)
53 {
54 double tt = fmod (aa, bb);
55 aa = bb;
56 bb = tt;
57 }
58
59 return aa;
60}
61
62template <typename FP>
63static void
64divide (const std::complex<FP>& a, const std::complex<FP>& b,
65 std::complex<FP>& q, std::complex<FP>& r)
66{
67 FP qr = std::floor ((a/b).real () + 0.5);
68 FP qi = std::floor ((a/b).imag () + 0.5);
69
70 q = std::complex<FP> (qr, qi);
71
72 r = a - q*b;
73}
74
75template <typename FP>
76static std::complex<FP>
77simple_gcd (const std::complex<FP>& a, const std::complex<FP>& b)
78{
79 if (! math::isinteger (a.real ())
80 || ! math::isinteger (a.imag ())
81 || ! math::isinteger (b.real ())
82 || ! math::isinteger (b.imag ()))
83 error ("gcd: all complex parts must be integers");
84
85 std::complex<FP> aa = a;
86 std::complex<FP> bb = b;
87
88 if (abs (aa) < abs (bb))
89 std::swap (aa, bb);
90
91 while (abs (bb) != 0)
92 {
93 std::complex<FP> qq, rr;
94 divide (aa, bb, qq, rr);
95 aa = bb;
96 bb = rr;
97 }
98
99 return aa;
100}
101
102template <typename T>
103static octave_int<T>
104simple_gcd (const octave_int<T>& a, const octave_int<T>& b)
105{
106 T aa = a.abs ().value ();
107 T bb = b.abs ().value ();
108
109 while (bb != 0)
110 {
111 T tt = aa % bb;
112 aa = bb;
113 bb = tt;
114 }
115
116 return aa;
117}
118
119static double
120extended_gcd (double a, double b, double& x, double& y)
121{
122 if (! math::isinteger (a) || ! math::isinteger (b))
123 error ("gcd: all values must be integers");
124
125 double aa = fabs (a);
126 double bb = fabs (b);
127
128 double xx, lx, yy, ly;
129 xx = 0, lx = 1;
130 yy = 1, ly = 0;
131
132 while (bb != 0)
133 {
134 double qq = std::floor (aa / bb);
135 double tt = fmod (aa, bb);
136
137 aa = bb;
138 bb = tt;
139
140 double tx = lx - qq*xx;
141 lx = xx;
142 xx = tx;
143
144 double ty = ly - qq*yy;
145 ly = yy;
146 yy = ty;
147 }
148
149 x = (a >= 0 ? lx : -lx);
150 y = (b >= 0 ? ly : -ly);
151
152 return aa;
153}
154
155template <typename FP>
156static std::complex<FP>
157extended_gcd (const std::complex<FP>& a, const std::complex<FP>& b,
158 std::complex<FP>& x, std::complex<FP>& y)
159{
160 if (! math::isinteger (a.real ())
161 || ! math::isinteger (a.imag ())
162 || ! math::isinteger (b.real ())
163 || ! math::isinteger (b.imag ()))
164 error ("gcd: all complex parts must be integers");
165
166 std::complex<FP> aa = a;
167 std::complex<FP> bb = b;
168 bool swapped = false;
169 if (abs (aa) < abs (bb))
170 {
171 std::swap (aa, bb);
172 swapped = true;
173 }
174
175 std::complex<FP> xx, lx, yy, ly;
176 xx = 0, lx = 1;
177 yy = 1, ly = 0;
178
179 while (abs(bb) != 0)
180 {
181 std::complex<FP> qq, rr;
182 divide (aa, bb, qq, rr);
183 aa = bb;
184 bb = rr;
185
186 std::complex<FP> tx = lx - qq*xx;
187 lx = xx;
188 xx = tx;
189
190 std::complex<FP> ty = ly - qq*yy;
191 ly = yy;
192 yy = ty;
193 }
194
195 x = lx;
196 y = ly;
197
198 if (swapped)
199 std::swap (x, y);
200
201 return aa;
202}
203
204template <typename T>
205static octave_int<T>
206extended_gcd (const octave_int<T>& a, const octave_int<T>& b,
208{
209 T aa = a.abs ().value ();
210 T bb = b.abs ().value ();
211 T xx, lx, yy, ly;
212 xx = 0, lx = 1;
213 yy = 1, ly = 0;
214
215 while (bb != 0)
216 {
217 T qq = aa / bb;
218 T tt = aa % bb;
219 aa = bb;
220 bb = tt;
221
222 T tx = lx - qq*xx;
223 lx = xx;
224 xx = tx;
225
226 T ty = ly - qq*yy;
227 ly = yy;
228 yy = ty;
229 }
230
231 x = octave_int<T> (lx) * a.signum ();
232 y = octave_int<T> (ly) * b.signum ();
233
234 return aa;
235}
236
237template <typename NDA>
238static octave_value
239do_simple_gcd (const octave_value& a, const octave_value& b)
240{
241 typedef typename NDA::element_type T;
242 octave_value retval;
243
244 if (a.is_scalar_type () && b.is_scalar_type ())
245 {
246 // Optimize scalar case.
247 T aa = octave_value_extract<T> (a);
248 T bb = octave_value_extract<T> (b);
249 retval = simple_gcd (aa, bb);
250 }
251 else
252 {
253 NDA aa = octave_value_extract<NDA> (a);
254 NDA bb = octave_value_extract<NDA> (b);
255 retval = binmap<T> (aa, bb, simple_gcd, "gcd");
256 }
257
258 return retval;
259}
260
261// Dispatcher
262static octave_value
263do_simple_gcd (const octave_value& a, const octave_value& b)
264{
265 octave_value retval;
267 b.builtin_type ());
268 switch (btyp)
269 {
270 case btyp_double:
271 if (a.issparse () && b.issparse ())
272 {
273 retval = do_simple_gcd<SparseMatrix> (a, b);
274 break;
275 }
276 OCTAVE_FALLTHROUGH;
277
278 case btyp_float:
279 retval = do_simple_gcd<NDArray> (a, b);
280 break;
281
282#define MAKE_INT_BRANCH(X) \
283 case btyp_ ## X: \
284 retval = do_simple_gcd<X ## NDArray> (a, b); \
285 break
286
287 MAKE_INT_BRANCH (int8);
288 MAKE_INT_BRANCH (int16);
289 MAKE_INT_BRANCH (int32);
290 MAKE_INT_BRANCH (int64);
291 MAKE_INT_BRANCH (uint8);
292 MAKE_INT_BRANCH (uint16);
293 MAKE_INT_BRANCH (uint32);
294 MAKE_INT_BRANCH (uint64);
295
296#undef MAKE_INT_BRANCH
297
298 case btyp_complex:
299 retval = do_simple_gcd<ComplexNDArray> (a, b);
300 break;
301
303 retval = do_simple_gcd<FloatComplexNDArray> (a, b);
304 break;
305
306 default:
307 error ("gcd: invalid class combination for gcd: %s and %s\n",
308 a.class_name ().c_str (), b.class_name ().c_str ());
309 }
310
311 if (btyp == btyp_float)
312 retval = retval.float_array_value ();
313
314 return retval;
315}
316
317template <typename NDA>
318static octave_value
319do_extended_gcd (const octave_value& a, const octave_value& b,
321{
322 typedef typename NDA::element_type T;
323 octave_value retval;
324
325 if (a.is_scalar_type () && b.is_scalar_type ())
326 {
327 // Optimize scalar case.
328 T aa = octave_value_extract<T> (a);
329 T bb = octave_value_extract<T> (b);
330 T xx, yy;
331 retval = extended_gcd (aa, bb, xx, yy);
332 x = xx;
333 y = yy;
334 }
335 else
336 {
337 NDA aa = octave_value_extract<NDA> (a);
338 NDA bb = octave_value_extract<NDA> (b);
339
340 dim_vector dv = aa.dims ();
341 if (aa.numel () == 1)
342 dv = bb.dims ();
343 else if (bb.numel () != 1 && bb.dims () != dv)
344 err_nonconformant ("gcd", a.dims (), b.dims ());
345
346 NDA gg (dv), xx (dv), yy (dv);
347
348 const T *aptr = aa.data ();
349 const T *bptr = bb.data ();
350
351 bool inca = aa.numel () != 1;
352 bool incb = bb.numel () != 1;
353
354 T *gptr = gg.rwdata ();
355 T *xptr = xx.rwdata ();
356 T *yptr = yy.rwdata ();
357
358 octave_idx_type n = gg.numel ();
359 for (octave_idx_type i = 0; i < n; i++)
360 {
361 octave_quit ();
362
363 *gptr++ = extended_gcd (*aptr, *bptr, *xptr++, *yptr++);
364
365 aptr += inca;
366 bptr += incb;
367 }
368
369 x = xx;
370 y = yy;
371
372 retval = gg;
373 }
374
375 return retval;
376}
377
378// Dispatcher
379static octave_value
380do_extended_gcd (const octave_value& a, const octave_value& b,
382{
383 octave_value retval;
384
386 b.builtin_type ());
387 switch (btyp)
388 {
389 case btyp_double:
390 case btyp_float:
391 retval = do_extended_gcd<NDArray> (a, b, x, y);
392 break;
393
394#define MAKE_INT_BRANCH(X) \
395 case btyp_ ## X: \
396 retval = do_extended_gcd<X ## NDArray> (a, b, x, y); \
397 break
398
399 MAKE_INT_BRANCH (int8);
400 MAKE_INT_BRANCH (int16);
401 MAKE_INT_BRANCH (int32);
402 MAKE_INT_BRANCH (int64);
403 MAKE_INT_BRANCH (uint8);
404 MAKE_INT_BRANCH (uint16);
405 MAKE_INT_BRANCH (uint32);
406 MAKE_INT_BRANCH (uint64);
407
408#undef MAKE_INT_BRANCH
409
410 case btyp_complex:
411 retval = do_extended_gcd<ComplexNDArray> (a, b, x, y);
412 break;
413
415 retval = do_extended_gcd<FloatComplexNDArray> (a, b, x, y);
416 break;
417
418 default:
419 error ("gcd: invalid class combination for gcd: %s and %s\n",
420 a.class_name ().c_str (), b.class_name ().c_str ());
421 }
422
423 // For consistency.
424 if (a.issparse () && b.issparse ())
425 {
426 retval = retval.sparse_matrix_value ();
427 x = x.sparse_matrix_value ();
428 y = y.sparse_matrix_value ();
429 }
430
431 if (btyp == btyp_float)
432 {
433 retval = retval.float_array_value ();
434 x = x.float_array_value ();
435 y = y.float_array_value ();
436 }
437
438 return retval;
439}
440
441DEFUN (gcd, args, nargout,
442 doc: /* -*- texinfo -*-
443@deftypefn {} {@var{g} =} gcd (@var{a1}, @var{a2}, @dots{})
444@deftypefnx {} {[@var{g}, @var{v1}, @dots{}] =} gcd (@var{a1}, @var{a2}, @dots{})
445Compute the greatest common divisor of @var{a1}, @var{a2}, @dots{}.
446
447All arguments must be the same size or scalar. For arrays, the greatest common
448divisor is calculated for each element individually. All elements must be
449ordinary or Gaussian (complex) integers. Note that for Gaussian integers, the
450gcd is only unique up to a phase factor (multiplication by 1, -1, i, or -i), so
451an arbitrary greatest common divisor among the four possible is returned.
452
453Optional return arguments @var{v1}, @dots{}, contain integer vectors such
454that,
455
456@tex
457$g = v_1 a_1 + v_2 a_2 + \cdots$
458@end tex
459@ifnottex
460
461@example
462@var{g} = @var{v1} .* @var{a1} + @var{v2} .* @var{a2} + @dots{}
463@end example
464
465@end ifnottex
466
467Example code:
468
469@example
470@group
471gcd ([15, 9], [20, 18])
472 @result{} 5 9
473@end group
474@end example
475
476Programming tip: To find the GCD of all the elements of a single array, use
477@code{num2cell} instead of nested calls or a loop:
478
479@example
480@group
481x = [30 42 70 105]; # vector or array of inputs
482gcd (num2cell (x) @{:@})
483 @result{} 1
484@end group
485@end example
486
487@seealso{lcm, factor, isprime}
488@end deftypefn */)
489{
490 int nargin = args.length ();
491
492 if (nargin < 2)
493 print_usage ();
494
495 octave_value_list retval;
496
497 if (nargout > 1)
498 {
499 retval.resize (nargin + 1);
500
501 retval(0) = do_extended_gcd (args(0), args(1), retval(1), retval(2));
502
503 for (int j = 2; j < nargin; j++)
504 {
506 retval(0) = do_extended_gcd (retval(0), args(j), x, retval(j+1));
507 for (int i = 0; i < j; i++)
508 retval(i+1).assign (octave_value::op_el_mul_eq, x);
509 }
510 }
511 else
512 {
513 retval(0) = do_simple_gcd (args(0), args(1));
514
515 for (int j = 2; j < nargin; j++)
516 retval(0) = do_simple_gcd (retval(0), args(j));
517 }
518
519 return retval;
520}
521
522/*
523%!assert (gcd (200, 300, 50, 35), 5)
524%!assert (gcd (int16 (200), int16 (300), int16 (50), int16 (35)), int16 (5))
525%!assert (gcd (uint64 (200), uint64 (300), uint64 (50), uint64 (35)),
526%! uint64 (5))
527%!assert (gcd (18-i, -29+3i), -3-4i)
528
529%!test
530%! p = [953 967];
531%! u = [953 + i*971, 967 + i*977];
532%! [d, k(1), k(2)] = gcd (p(1), p(2));
533%! [z, w(1), w(2)] = gcd (u(1), u(2));
534%! assert (d, 1);
535%! assert (sum (p.*k), d);
536%! assert (abs (z), sqrt (2));
537%! assert (abs (sum (u.*w)), sqrt (2));
538
539%!error <all values must be integers> gcd (1/2, 2)
540%!error <all complex parts must be integers> gcd (e + i*pi, 1)
541
542%!error gcd ()
543
544%!test
545%! s.a = 1;
546%! fail ("gcd (s)");
547*/
548
549OCTAVE_END_NAMESPACE(octave)
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
octave_int< T > abs() const
octave_int< T > signum() const
void resize(octave_idx_type n, const octave_value &rfv=octave_value())
Definition ovl.h:115
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition ov.h:909
std::string class_name() const
Definition ov.h:1362
bool is_scalar_type() const
Definition ov.h:744
@ op_el_mul_eq
Definition ov.h:142
bool issparse() const
Definition ov.h:753
octave_idx_type length() const
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition ov.h:868
builtin_type_t builtin_type() const
Definition ov.h:690
dim_vector dims() const
Definition ov.h:541
Definition qr.h:39
ColumnVector real(const ComplexColumnVector &a)
ColumnVector imag(const ComplexColumnVector &a)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage()
Definition defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition defun.h:56
void error(const char *fmt,...)
Definition error.cc:1003
void err_nonconformant()
Definition errwarn.cc:95
#define MAKE_INT_BRANCH(X)
F77_RET_T const F77_DBLE * x
builtin_type_t btyp_mixed_numeric(builtin_type_t x, builtin_type_t y)
Determine the resulting type for a possible mixed-type operation.
Definition ov-base.cc:67
builtin_type_t
Definition ov-base.h:83
@ btyp_float_complex
Definition ov-base.h:87
@ btyp_double
Definition ov-base.h:84
@ btyp_float
Definition ov-base.h:85
@ btyp_complex
Definition ov-base.h:86
template int8_t abs(int8_t)