GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
gcd.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2004-2024 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 
43 static double
44 simple_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 
62 template <typename FP>
63 static void
64 divide (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 
75 template <typename FP>
76 static std::complex<FP>
77 simple_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 
102 template <typename T>
103 static octave_int<T>
104 simple_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 
119 static double
120 extended_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 
155 template <typename FP>
156 static std::complex<FP>
157 extended_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 
204 template <typename T>
205 static octave_int<T>
206 extended_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 
237 template <typename NDA>
238 static octave_value
239 do_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
262 static octave_value
263 do_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 
302  case btyp_float_complex:
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 
317 template <typename NDA>
318 static octave_value
319 do_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.fortran_vec ();
355  T *xptr = xx.fortran_vec ();
356  T *yptr = yy.fortran_vec ();
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
379 static octave_value
380 do_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 
414  case btyp_float_complex:
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 
441 DEFUN (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{})
445 Compute the greatest common divisor of @var{a1}, @var{a2}, @dots{}.
446 
447 All arguments must be the same size or scalar. For arrays, the greatest common
448 divisor is calculated for each element individually. All elements must be
449 ordinary or Gaussian (complex) integers. Note that for Gaussian integers, the
450 gcd is only unique up to a phase factor (multiplication by 1, -1, i, or -i), so
451 an arbitrary greatest common divisor among the four possible is returned.
452 
453 Optional return arguments @var{v1}, @dots{}, contain integer vectors such
454 that,
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 
467 Example code:
468 
469 @example
470 @group
471 gcd ([15, 9], [20, 18])
472  @result{} 5 9
473 @end group
474 @end example
475 
476 Programming 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
481 x = [30 42 70 105]; # vector or array of inputs
482 gcd (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  {
505  octave_value x;
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 
549 OCTAVE_END_NAMESPACE(octave)
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
octave_int< T > signum() const
Definition: oct-inttypes.h:863
octave_int< T > abs() const
Definition: oct-inttypes.h:862
void resize(octave_idx_type n, const octave_value &rfv=octave_value())
Definition: ovl.h:117
SparseMatrix sparse_matrix_value(bool frc_str_conv=false) const
Definition: ov.h:900
std::string class_name() const
Definition: ov.h:1347
bool is_scalar_type() const
Definition: ov.h:744
@ op_el_mul_eq
Definition: ov.h:142
bool issparse() const
Definition: ov.h:753
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition: ov.h:862
builtin_type_t builtin_type() const
Definition: ov.h:690
dim_vector dims() const
Definition: ov.h:541
Definition: qr.h:40
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:137
ColumnVector imag(const ComplexColumnVector &a)
Definition: dColVector.cc:143
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
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:988
#define MAKE_INT_BRANCH(X)
void err_nonconformant(const char *op, octave_idx_type op1_len, octave_idx_type op2_len)
bool isinteger(double x)
Definition: lo-mappers.h:225
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:761
T * r
Definition: mx-inlines.cc:781
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)