Mercurial > octave
diff src/corefcn/gcd.cc @ 15039:e753177cde93
maint: Move non-dynamically linked functions from DLD-FUNCTIONS/ to corefcn/ directory
* __contourc__.cc, __dispatch__.cc, __lin_interpn__.cc, __pchip_deriv__.cc,
__qp__.cc, balance.cc, besselj.cc, betainc.cc, bsxfun.cc, cellfun.cc,
colloc.cc, conv2.cc, daspk.cc, dasrt.cc, dassl.cc, det.cc, dlmread.cc, dot.cc,
eig.cc, fft.cc, fft2.cc, fftn.cc, filter.cc, find.cc, gammainc.cc, gcd.cc,
getgrent.cc, getpwent.cc, getrusage.cc, givens.cc, hess.cc, hex2num.cc, inv.cc,
kron.cc, lookup.cc, lsode.cc, lu.cc, luinc.cc, matrix_type.cc, max.cc,
md5sum.cc, mgorth.cc, nproc.cc, pinv.cc, quad.cc, quadcc.cc, qz.cc,
rand.cc, rcond.cc, regexp.cc, schur.cc, spparms.cc, sqrtm.cc, str2double.cc,
strfind.cc, sub2ind.cc, svd.cc, syl.cc, time.cc, tril.cc, typecast.cc:
Move functions from DLD-FUNCTIONS/ to corefcn/ directory. Include "defun.h",
not "defun-dld.h". Change docstring to refer to these as "Built-in Functions".
* build-aux/mk-opts.pl: Generate options code with '#include "defun.h"'. Change
option docstrings to refer to these as "Built-in Functions".
* corefcn/module.mk: List of functions to build in corefcn/ dir.
* DLD-FUNCTIONS/config-module.awk: Update to new build system.
* DLD-FUNCTIONS/module-files: Remove functions which are now in corefcn/ directory.
* src/Makefile.am: Update to build "convenience library" in corefcn/. Octave
program now links against all other libraries + corefcn libary.
* src/find-defun-files.sh: Strip $srcdir from filename.
* src/link-deps.mk: Add REGEX and FFTW link dependencies for liboctinterp.
* type.m, which.m: Change failing tests to use 'amd', still a dynamic function,
rather than 'dot', which isn't.
author | Rik <rik@octave.org> |
---|---|
date | Fri, 27 Jul 2012 15:35:00 -0700 |
parents | src/DLD-FUNCTIONS/gcd.cc@5ae9f0f77635 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/corefcn/gcd.cc Fri Jul 27 15:35:00 2012 -0700 @@ -0,0 +1,528 @@ +/* + +Copyright (C) 2004-2012 David Bateman +Copyright (C) 2010 Jaroslav Hajek, Jordi GutiƩrrez Hermoso + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, see +<http://www.gnu.org/licenses/>. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "dNDArray.h" +#include "CNDArray.h" +#include "fNDArray.h" +#include "fCNDArray.h" +#include "lo-mappers.h" +#include "oct-binmap.h" + +#include "defun.h" +#include "error.h" +#include "oct-obj.h" + +static double +simple_gcd (double a, double b) +{ + if (! xisinteger (a) || ! xisinteger (b)) + (*current_liboctave_error_handler) + ("gcd: all values must be integers"); + + double aa = fabs (a); + double bb = fabs (b); + + while (bb != 0) + { + double tt = fmod (aa, bb); + aa = bb; + bb = tt; + } + + return aa; +} + +// Don't use the Complex and FloatComplex typedefs because we need to +// refer to the actual float precision FP in the body (and when gcc +// implements template aliases from C++0x, can do a small fix here). +template <typename FP> +static void +divide (const std::complex<FP>& a, const std::complex<FP>& b, + std::complex<FP>& q, std::complex<FP>& r) +{ + FP qr = gnulib::floor ((a/b).real () + 0.5); + FP qi = gnulib::floor ((a/b).imag () + 0.5); + + q = std::complex<FP> (qr, qi); + + r = a - q*b; +} + +template <typename FP> +static std::complex<FP> +simple_gcd (const std::complex<FP>& a, const std::complex<FP>& b) +{ + if (! xisinteger (a.real ()) || ! xisinteger (a.imag ()) + || ! xisinteger (b.real ()) || ! xisinteger (b.imag ())) + (*current_liboctave_error_handler) + ("gcd: all complex parts must be integers"); + + std::complex<FP> aa = a; + std::complex<FP> bb = b; + + if (abs (aa) < abs (bb)) + std::swap (aa, bb); + + while (abs (bb) != 0) + { + std::complex<FP> qq, rr; + divide (aa, bb, qq, rr); + aa = bb; + bb = rr; + } + + return aa; +} + +template <class T> +static octave_int<T> +simple_gcd (const octave_int<T>& a, const octave_int<T>& b) +{ + T aa = a.abs ().value (); + T bb = b.abs ().value (); + + while (bb != 0) + { + T tt = aa % bb; + aa = bb; + bb = tt; + } + + return aa; +} + +static double +extended_gcd (double a, double b, double& x, double& y) +{ + if (! xisinteger (a) || ! xisinteger (b)) + (*current_liboctave_error_handler) + ("gcd: all values must be integers"); + + double aa = fabs (a); + double bb = fabs (b); + + double xx = 0, yy = 1; + double lx = 1, ly = 0; + + while (bb != 0) + { + double qq = gnulib::floor (aa / bb); + double tt = fmod (aa, bb); + + aa = bb; + bb = tt; + + double tx = lx - qq*xx; + lx = xx; + xx = tx; + + double ty = ly - qq*yy; + ly = yy; + yy = ty; + } + + x = a >= 0 ? lx : -lx; + y = b >= 0 ? ly : -ly; + + return aa; +} + +template <typename FP> +static std::complex<FP> +extended_gcd (const std::complex<FP>& a, const std::complex<FP>& b, + std::complex<FP>& x, std::complex<FP>& y) +{ + if (! xisinteger (a.real ()) || ! xisinteger (a.imag ()) + || ! xisinteger (b.real ()) || ! xisinteger (b.imag ())) + (*current_liboctave_error_handler) + ("gcd: all complex parts must be integers"); + + std::complex<FP> aa = a, bb = b; + bool swapped = false; + if (abs (aa) < abs (bb)) + { + std::swap (aa, bb); + swapped = true; + } + + std::complex<FP> xx = 0, lx = 1; + std::complex<FP> yy = 1, ly = 0; + + while (abs(bb) != 0) + { + std::complex<FP> qq, rr; + divide (aa, bb, qq, rr); + aa = bb; + bb = rr; + + std::complex<FP> tx = lx - qq*xx; + lx = xx; + xx = tx; + + std::complex<FP> ty = ly - qq*yy; + ly = yy; + yy = ty; + } + + x = lx; + y = ly; + + if (swapped) + std::swap (x, y); + + return aa; +} + +template <class T> +static octave_int<T> +extended_gcd (const octave_int<T>& a, const octave_int<T>& b, + octave_int<T>& x, octave_int<T>& y) +{ + T aa = a.abs ().value (); + T bb = b.abs ().value (); + T xx = 0, lx = 1; + T yy = 1, ly = 0; + + while (bb != 0) + { + T qq = aa / bb; + T tt = aa % bb; + aa = bb; + bb = tt; + + T tx = lx - qq*xx; + lx = xx; + xx = tx; + + T ty = ly - qq*yy; + ly = yy; + yy = ty; + } + + x = octave_int<T> (lx) * a.signum (); + y = octave_int<T> (ly) * b.signum (); + + return aa; +} + +template<class NDA> +static octave_value +do_simple_gcd (const octave_value& a, const octave_value& b) +{ + typedef typename NDA::element_type T; + octave_value retval; + + if (a.is_scalar_type () && b.is_scalar_type ()) + { + // Optimize scalar case. + T aa = octave_value_extract<T> (a); + T bb = octave_value_extract<T> (b); + retval = simple_gcd (aa, bb); + } + else + { + NDA aa = octave_value_extract<NDA> (a); + NDA bb = octave_value_extract<NDA> (b); + retval = binmap<T> (aa, bb, simple_gcd, "gcd"); + } + + return retval; +} + +// Dispatcher +static octave_value +do_simple_gcd (const octave_value& a, const octave_value& b) +{ + octave_value retval; + builtin_type_t btyp = btyp_mixed_numeric (a.builtin_type (), + b.builtin_type ()); + switch (btyp) + { + case btyp_double: + if (a.is_sparse_type () && b.is_sparse_type ()) + { + retval = do_simple_gcd<SparseMatrix> (a, b); + break; + } + // fall through! + + case btyp_float: + retval = do_simple_gcd<NDArray> (a, b); + break; + +#define MAKE_INT_BRANCH(X) \ + case btyp_ ## X: \ + retval = do_simple_gcd<X ## NDArray> (a, b); \ + break + + MAKE_INT_BRANCH (int8); + MAKE_INT_BRANCH (int16); + MAKE_INT_BRANCH (int32); + MAKE_INT_BRANCH (int64); + MAKE_INT_BRANCH (uint8); + MAKE_INT_BRANCH (uint16); + MAKE_INT_BRANCH (uint32); + MAKE_INT_BRANCH (uint64); + +#undef MAKE_INT_BRANCH + + case btyp_complex: + retval = do_simple_gcd<ComplexNDArray> (a, b); + break; + + case btyp_float_complex: + retval = do_simple_gcd<FloatComplexNDArray> (a, b); + break; + + default: + error ("gcd: invalid class combination for gcd: %s and %s\n", + a.class_name ().c_str (), b.class_name ().c_str ()); + } + + if (btyp == btyp_float) + retval = retval.float_array_value (); + + return retval; +} + +template<class NDA> +static octave_value +do_extended_gcd (const octave_value& a, const octave_value& b, + octave_value& x, octave_value& y) +{ + typedef typename NDA::element_type T; + octave_value retval; + + if (a.is_scalar_type () && b.is_scalar_type ()) + { + // Optimize scalar case. + T aa = octave_value_extract<T> (a); + T bb = octave_value_extract<T> (b); + T xx, yy; + retval = extended_gcd (aa, bb, xx, yy); + x = xx; + y = yy; + } + else + { + NDA aa = octave_value_extract<NDA> (a); + NDA bb = octave_value_extract<NDA> (b); + + dim_vector dv = aa.dims (); + if (aa.numel () == 1) + dv = bb.dims (); + else if (bb.numel () != 1 && bb.dims () != dv) + gripe_nonconformant ("gcd", a.dims (), b.dims ()); + + NDA gg (dv), xx (dv), yy (dv); + + const T *aptr = aa.fortran_vec (); + const T *bptr = bb.fortran_vec (); + + bool inca = aa.numel () != 1; + bool incb = bb.numel () != 1; + + T *gptr = gg.fortran_vec (); + T *xptr = xx.fortran_vec (), *yptr = yy.fortran_vec (); + + octave_idx_type n = gg.numel (); + for (octave_idx_type i = 0; i < n; i++) + { + octave_quit (); + + *gptr++ = extended_gcd (*aptr, *bptr, *xptr++, *yptr++); + + aptr += inca; + bptr += incb; + } + + x = xx; + y = yy; + + retval = gg; + } + + return retval; +} + +// Dispatcher +static octave_value +do_extended_gcd (const octave_value& a, const octave_value& b, + octave_value& x, octave_value& y) +{ + octave_value retval; + + builtin_type_t btyp = btyp_mixed_numeric (a.builtin_type (), + b.builtin_type ()); + switch (btyp) + { + case btyp_double: + case btyp_float: + retval = do_extended_gcd<NDArray> (a, b, x, y); + break; + +#define MAKE_INT_BRANCH(X) \ + case btyp_ ## X: \ + retval = do_extended_gcd<X ## NDArray> (a, b, x, y); \ + break + + MAKE_INT_BRANCH (int8); + MAKE_INT_BRANCH (int16); + MAKE_INT_BRANCH (int32); + MAKE_INT_BRANCH (int64); + MAKE_INT_BRANCH (uint8); + MAKE_INT_BRANCH (uint16); + MAKE_INT_BRANCH (uint32); + MAKE_INT_BRANCH (uint64); + +#undef MAKE_INT_BRANCH + + case btyp_complex: + retval = do_extended_gcd<ComplexNDArray> (a, b, x, y); + break; + + case btyp_float_complex: + retval = do_extended_gcd<FloatComplexNDArray> (a, b, x, y); + break; + + default: + error ("gcd: invalid class combination for gcd: %s and %s\n", + a.class_name ().c_str (), b.class_name ().c_str ()); + } + + // For consistency. + if (! error_state && a.is_sparse_type () && b.is_sparse_type ()) + { + retval = retval.sparse_matrix_value (); + x = x.sparse_matrix_value (); + y = y.sparse_matrix_value (); + } + + if (btyp == btyp_float) + { + retval = retval.float_array_value (); + x = x.float_array_value (); + y = y.float_array_value (); + } + + return retval; +} + +DEFUN (gcd, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {@var{g} =} gcd (@var{a1}, @var{a2}, @dots{})\n\ +@deftypefnx {Built-in Function} {[@var{g}, @var{v1}, @dots{}] =} gcd (@var{a1}, @var{a2}, @dots{})\n\ +\n\ +Compute the greatest common divisor of @var{a1}, @var{a2}, @dots{}. If more\n\ +than one argument is given all arguments must be the same size or scalar.\n\ +In this case the greatest common divisor is calculated for each element\n\ +individually. All elements must be ordinary or Gaussian (complex)\n\ +integers. Note that for Gaussian integers, the gcd is not unique up to\n\ +units (multiplication by 1, -1, @var{i} or -@var{i}), so an arbitrary\n\ +greatest common divisor amongst four possible is returned.\n\ +\n\ +Example code:\n\ +\n\ +@example\n\ +@group\n\ +gcd ([15, 9], [20, 18])\n\ + @result{} 5 9\n\ +@end group\n\ +@end example\n\ +\n\ +Optional return arguments @var{v1}, etc., contain integer vectors such\n\ +that,\n\ +\n\ +@tex\n\ +$g = v_1 a_1 + v_2 a_2 + \\cdots$\n\ +@end tex\n\ +@ifnottex\n\ +\n\ +@example\n\ +@var{g} = @var{v1} .* @var{a1} + @var{v2} .* @var{a2} + @dots{}\n\ +@end example\n\ +\n\ +@end ifnottex\n\ +\n\ +@seealso{lcm, factor}\n\ +@end deftypefn") +{ + octave_value_list retval; + + int nargin = args.length (); + + if (nargin > 1) + { + if (nargout > 1) + { + retval.resize (nargin + 1); + + retval(0) = do_extended_gcd (args(0), args(1), retval(1), retval(2)); + + for (int j = 2; j < nargin; j++) + { + octave_value x; + retval(0) = do_extended_gcd (retval(0), args(j), + x, retval(j+1)); + for (int i = 0; i < j; i++) + retval(i+1).assign (octave_value::op_el_mul_eq, x); + + if (error_state) + break; + } + } + else + { + retval(0) = do_simple_gcd (args(0), args(1)); + + for (int j = 2; j < nargin; j++) + { + retval(0) = do_simple_gcd (retval(0), args(j)); + + if (error_state) + break; + } + } + } + else + print_usage (); + + return retval; +} + +/* +%!assert (gcd (200, 300, 50, 35), 5) +%!assert (gcd (int16 (200), int16 (300), int16 (50), int16 (35)), int16 (5)) +%!assert (gcd (uint64 (200), uint64 (300), uint64 (50), uint64 (35)), uint64 (5)) +%!assert (gcd (18-i, -29+3i), -3-4i) + +%!error gcd () + +%!test +%! s.a = 1; +%! fail ("gcd (s)"); +*/