GNU Octave 7.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-2022 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
41OCTAVE_NAMESPACE_BEGIN
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
62// Don't use the Complex and FloatComplex typedefs because we need to
63// refer to the actual float precision FP in the body (and when gcc
64// implements template aliases from C++0x, can do a small fix here).
65template <typename FP>
66static void
67divide (const std::complex<FP>& a, const std::complex<FP>& b,
68 std::complex<FP>& q, std::complex<FP>& r)
69{
70 FP qr = std::floor ((a/b).real () + 0.5);
71 FP qi = std::floor ((a/b).imag () + 0.5);
72
73 q = std::complex<FP> (qr, qi);
74
75 r = a - q*b;
76}
77
78template <typename FP>
79static std::complex<FP>
80simple_gcd (const std::complex<FP>& a, const std::complex<FP>& b)
81{
82 if (! math::isinteger (a.real ())
83 || ! math::isinteger (a.imag ())
84 || ! math::isinteger (b.real ())
85 || ! math::isinteger (b.imag ()))
86 error ("gcd: all complex parts must be integers");
87
88 std::complex<FP> aa = a;
89 std::complex<FP> bb = b;
90
91 if (abs (aa) < abs (bb))
92 std::swap (aa, bb);
93
94 while (abs (bb) != 0)
95 {
96 std::complex<FP> qq, rr;
97 divide (aa, bb, qq, rr);
98 aa = bb;
99 bb = rr;
100 }
101
102 return aa;
103}
104
105template <typename T>
106static octave_int<T>
108{
109 T aa = a.abs ().value ();
110 T bb = b.abs ().value ();
111
112 while (bb != 0)
113 {
114 T tt = aa % bb;
115 aa = bb;
116 bb = tt;
117 }
118
119 return aa;
120}
121
122static double
123extended_gcd (double a, double b, double& x, double& y)
124{
125 if (! math::isinteger (a) || ! math::isinteger (b))
126 error ("gcd: all values must be integers");
127
128 double aa = fabs (a);
129 double bb = fabs (b);
130
131 double xx, lx, yy, ly;
132 xx = 0, lx = 1;
133 yy = 1, ly = 0;
134
135 while (bb != 0)
136 {
137 double qq = std::floor (aa / bb);
138 double tt = fmod (aa, bb);
139
140 aa = bb;
141 bb = tt;
142
143 double tx = lx - qq*xx;
144 lx = xx;
145 xx = tx;
146
147 double ty = ly - qq*yy;
148 ly = yy;
149 yy = ty;
150 }
151
152 x = (a >= 0 ? lx : -lx);
153 y = (b >= 0 ? ly : -ly);
154
155 return aa;
156}
157
158template <typename FP>
159static std::complex<FP>
160extended_gcd (const std::complex<FP>& a, const std::complex<FP>& b,
161 std::complex<FP>& x, std::complex<FP>& y)
162{
163 if (! math::isinteger (a.real ())
164 || ! math::isinteger (a.imag ())
165 || ! math::isinteger (b.real ())
166 || ! math::isinteger (b.imag ()))
167 error ("gcd: all complex parts must be integers");
168
169 std::complex<FP> aa = a;
170 std::complex<FP> bb = b;
171 bool swapped = false;
172 if (abs (aa) < abs (bb))
173 {
174 std::swap (aa, bb);
175 swapped = true;
176 }
177
178 std::complex<FP> xx, lx, yy, ly;
179 xx = 0, lx = 1;
180 yy = 1, ly = 0;
181
182 while (abs(bb) != 0)
183 {
184 std::complex<FP> qq, rr;
185 divide (aa, bb, qq, rr);
186 aa = bb;
187 bb = rr;
188
189 std::complex<FP> tx = lx - qq*xx;
190 lx = xx;
191 xx = tx;
192
193 std::complex<FP> ty = ly - qq*yy;
194 ly = yy;
195 yy = ty;
196 }
197
198 x = lx;
199 y = ly;
200
201 if (swapped)
202 std::swap (x, y);
203
204 return aa;
205}
206
207template <typename T>
208static octave_int<T>
211{
212 T aa = a.abs ().value ();
213 T bb = b.abs ().value ();
214 T xx, lx, yy, ly;
215 xx = 0, lx = 1;
216 yy = 1, ly = 0;
217
218 while (bb != 0)
219 {
220 T qq = aa / bb;
221 T tt = aa % bb;
222 aa = bb;
223 bb = tt;
224
225 T tx = lx - qq*xx;
226 lx = xx;
227 xx = tx;
228
229 T ty = ly - qq*yy;
230 ly = yy;
231 yy = ty;
232 }
233
234 x = octave_int<T> (lx) * a.signum ();
235 y = octave_int<T> (ly) * b.signum ();
236
237 return aa;
238}
239
240template <typename NDA>
241static octave_value
243{
244 typedef typename NDA::element_type T;
245 octave_value retval;
246
247 if (a.is_scalar_type () && b.is_scalar_type ())
248 {
249 // Optimize scalar case.
250 T aa = octave_value_extract<T> (a);
251 T bb = octave_value_extract<T> (b);
252 retval = simple_gcd (aa, bb);
253 }
254 else
255 {
256 NDA aa = octave_value_extract<NDA> (a);
257 NDA bb = octave_value_extract<NDA> (b);
258 retval = binmap<T> (aa, bb, simple_gcd, "gcd");
259 }
260
261 return retval;
262}
263
264// Dispatcher
265static octave_value
267{
268 octave_value retval;
270 b.builtin_type ());
271 switch (btyp)
272 {
273 case btyp_double:
274 if (a.issparse () && b.issparse ())
275 {
276 retval = do_simple_gcd<SparseMatrix> (a, b);
277 break;
278 }
279 OCTAVE_FALLTHROUGH;
280
281 case btyp_float:
282 retval = do_simple_gcd<NDArray> (a, b);
283 break;
284
285#define MAKE_INT_BRANCH(X) \
286 case btyp_ ## X: \
287 retval = do_simple_gcd<X ## NDArray> (a, b); \
288 break
289
290 MAKE_INT_BRANCH (int8);
291 MAKE_INT_BRANCH (int16);
292 MAKE_INT_BRANCH (int32);
293 MAKE_INT_BRANCH (int64);
294 MAKE_INT_BRANCH (uint8);
295 MAKE_INT_BRANCH (uint16);
296 MAKE_INT_BRANCH (uint32);
297 MAKE_INT_BRANCH (uint64);
298
299#undef MAKE_INT_BRANCH
300
301 case btyp_complex:
302 retval = do_simple_gcd<ComplexNDArray> (a, b);
303 break;
304
306 retval = do_simple_gcd<FloatComplexNDArray> (a, b);
307 break;
308
309 default:
310 error ("gcd: invalid class combination for gcd: %s and %s\n",
311 a.class_name ().c_str (), b.class_name ().c_str ());
312 }
313
314 if (btyp == btyp_float)
315 retval = retval.float_array_value ();
316
317 return retval;
318}
319
320template <typename NDA>
321static octave_value
324{
325 typedef typename NDA::element_type T;
326 octave_value retval;
327
328 if (a.is_scalar_type () && b.is_scalar_type ())
329 {
330 // Optimize scalar case.
331 T aa = octave_value_extract<T> (a);
332 T bb = octave_value_extract<T> (b);
333 T xx, yy;
334 retval = extended_gcd (aa, bb, xx, yy);
335 x = xx;
336 y = yy;
337 }
338 else
339 {
340 NDA aa = octave_value_extract<NDA> (a);
341 NDA bb = octave_value_extract<NDA> (b);
342
343 dim_vector dv = aa.dims ();
344 if (aa.numel () == 1)
345 dv = bb.dims ();
346 else if (bb.numel () != 1 && bb.dims () != dv)
347 err_nonconformant ("gcd", a.dims (), b.dims ());
348
349 NDA gg (dv), xx (dv), yy (dv);
350
351 const T *aptr = aa.data ();
352 const T *bptr = bb.data ();
353
354 bool inca = aa.numel () != 1;
355 bool incb = bb.numel () != 1;
356
357 T *gptr = gg.fortran_vec ();
358 T *xptr = xx.fortran_vec ();
359 T *yptr = yy.fortran_vec ();
360
361 octave_idx_type n = gg.numel ();
362 for (octave_idx_type i = 0; i < n; i++)
363 {
364 octave_quit ();
365
366 *gptr++ = extended_gcd (*aptr, *bptr, *xptr++, *yptr++);
367
368 aptr += inca;
369 bptr += incb;
370 }
371
372 x = xx;
373 y = yy;
374
375 retval = gg;
376 }
377
378 return retval;
379}
380
381// Dispatcher
382static octave_value
385{
386 octave_value retval;
387
389 b.builtin_type ());
390 switch (btyp)
391 {
392 case btyp_double:
393 case btyp_float:
394 retval = do_extended_gcd<NDArray> (a, b, x, y);
395 break;
396
397#define MAKE_INT_BRANCH(X) \
398 case btyp_ ## X: \
399 retval = do_extended_gcd<X ## NDArray> (a, b, x, y); \
400 break
401
402 MAKE_INT_BRANCH (int8);
403 MAKE_INT_BRANCH (int16);
404 MAKE_INT_BRANCH (int32);
405 MAKE_INT_BRANCH (int64);
406 MAKE_INT_BRANCH (uint8);
407 MAKE_INT_BRANCH (uint16);
408 MAKE_INT_BRANCH (uint32);
409 MAKE_INT_BRANCH (uint64);
410
411#undef MAKE_INT_BRANCH
412
413 case btyp_complex:
414 retval = do_extended_gcd<ComplexNDArray> (a, b, x, y);
415 break;
416
418 retval = do_extended_gcd<FloatComplexNDArray> (a, b, x, y);
419 break;
420
421 default:
422 error ("gcd: invalid class combination for gcd: %s and %s\n",
423 a.class_name ().c_str (), b.class_name ().c_str ());
424 }
425
426 // For consistency.
427 if (a.issparse () && b.issparse ())
428 {
429 retval = retval.sparse_matrix_value ();
430 x = x.sparse_matrix_value ();
431 y = y.sparse_matrix_value ();
432 }
433
434 if (btyp == btyp_float)
435 {
436 retval = retval.float_array_value ();
437 x = x.float_array_value ();
438 y = y.float_array_value ();
439 }
440
441 return retval;
442}
443
444DEFUN (gcd, args, nargout,
445 doc: /* -*- texinfo -*-
446@deftypefn {} {@var{g} =} gcd (@var{a1}, @var{a2}, @dots{})
447@deftypefnx {} {[@var{g}, @var{v1}, @dots{}] =} gcd (@var{a1}, @var{a2}, @dots{})
448Compute the greatest common divisor of @var{a1}, @var{a2}, @dots{}.
449
450All arguments must be the same size or scalar. For arrays, the greatest common
451divisor is calculated for each element individually. All elements must be
452ordinary or Gaussian (complex) integers. Note that for Gaussian integers, the
453gcd is only unique up to a phase factor (multiplication by 1, -1, i, or -i), so
454an arbitrary greatest common divisor among the four possible is returned.
455
456Optional return arguments @var{v1}, @dots{}, contain integer vectors such
457that,
458
459@tex
460$g = v_1 a_1 + v_2 a_2 + \cdots$
461@end tex
462@ifnottex
463
464@example
465@var{g} = @var{v1} .* @var{a1} + @var{v2} .* @var{a2} + @dots{}
466@end example
467
468@end ifnottex
469
470Example code:
471
472@example
473@group
474gcd ([15, 9], [20, 18])
475 @result{} 5 9
476@end group
477@end example
478
479@seealso{lcm, factor, isprime}
480@end deftypefn */)
481{
482 int nargin = args.length ();
483
484 if (nargin < 2)
485 print_usage ();
486
487 octave_value_list retval;
488
489 if (nargout > 1)
490 {
491 retval.resize (nargin + 1);
492
493 retval(0) = do_extended_gcd (args(0), args(1), retval(1), retval(2));
494
495 for (int j = 2; j < nargin; j++)
496 {
498 retval(0) = do_extended_gcd (retval(0), args(j), x, retval(j+1));
499 for (int i = 0; i < j; i++)
500 retval(i+1).assign (octave_value::op_el_mul_eq, x);
501 }
502 }
503 else
504 {
505 retval(0) = do_simple_gcd (args(0), args(1));
506
507 for (int j = 2; j < nargin; j++)
508 retval(0) = do_simple_gcd (retval(0), args(j));
509 }
510
511 return retval;
512}
513
514/*
515%!assert (gcd (200, 300, 50, 35), 5)
516%!assert (gcd (int16 (200), int16 (300), int16 (50), int16 (35)), int16 (5))
517%!assert (gcd (uint64 (200), uint64 (300), uint64 (50), uint64 (35)), uint64 (5))
518%!assert (gcd (18-i, -29+3i), -3-4i)
519
520%!test
521%! p = [953 967];
522%! u = [953 + i*971, 967 + i*977];
523%! [d, k(1), k(2)] = gcd (p(1), p(2));
524%! [z, w(1), w(2)] = gcd (u(1), u(2));
525%! assert (d, 1);
526%! assert (sum (p.*k), d);
527%! assert (abs (z), sqrt (2));
528%! assert (abs (sum (u.*w)), sqrt (2));
529
530%!error <all values must be integers> gcd (1/2, 2)
531%!error <all complex parts must be integers> gcd (e + i*pi, 1)
532
533%!error gcd ()
534
535%!test
536%! s.a = 1;
537%! fail ("gcd (s)");
538*/
539
540OCTAVE_NAMESPACE_END
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
octave_int< T > abs() const
Definition: oct-inttypes.h:860
octave_int< T > signum() const
Definition: oct-inttypes.h:861
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:945
bool issparse(void) const
Definition: ov.h:798
builtin_type_t builtin_type(void) const
Definition: ov.h:735
bool is_scalar_type(void) const
Definition: ov.h:789
std::string class_name(void) const
Definition: ov.h:1451
@ op_el_mul_eq
Definition: ov.h:140
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition: ov.h:907
dim_vector dims(void) const
Definition: ov.h:586
Definition: mx-defs.h:53
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:137
ColumnVector imag(const ComplexColumnVector &a)
Definition: dColVector.cc:143
OCTINTERP_API 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:980
static octave_value do_extended_gcd(const octave_value &a, const octave_value &b, octave_value &x, octave_value &y)
Definition: gcd.cc:322
static void divide(const std::complex< FP > &a, const std::complex< FP > &b, std::complex< FP > &q, std::complex< FP > &r)
Definition: gcd.cc:67
static octave_value do_simple_gcd(const octave_value &a, const octave_value &b)
Definition: gcd.cc:242
static double extended_gcd(double a, double b, double &x, double &y)
Definition: gcd.cc:123
#define MAKE_INT_BRANCH(X)
static OCTAVE_NAMESPACE_BEGIN double simple_gcd(double a, double b)
Definition: gcd.cc:44
F77_RET_T const F77_DBLE * x
bool isinteger(double x)
Definition: lo-mappers.h:225
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
void err_nonconformant(const char *op, octave_idx_type op1_len, octave_idx_type op2_len)
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:65
builtin_type_t
Definition: ov-base.h:75
@ btyp_float_complex
Definition: ov-base.h:79
@ btyp_double
Definition: ov-base.h:76
@ btyp_float
Definition: ov-base.h:77
@ btyp_complex
Definition: ov-base.h:78
static T abs(T x)
Definition: pr-output.cc:1678