Mercurial > hg > octave-avbm
changeset 11062:88d78ee12ee8
Implement gcd for Gaussian (complex) integers
author | Jordi Gutiérrez Hermoso <jordigh@gmail.com> |
---|---|
date | Thu, 30 Sep 2010 02:05:31 -0500 |
parents | 604dfbaf5010 |
children | e378c0176a38 |
files | src/ChangeLog src/DLD-FUNCTIONS/gcd.cc |
diffstat | 2 files changed, 109 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2010-09-30 Jordi Gutiérrez Hermoso <jordigh@gmail.com> + * DLD-FUNCTIONS/gcd.cc (divide): New function, complex integer + division with remainder. + (simple_gcd): Overload for complex values. + (extended_gcd): Ditto. + (do_simple_gcd): Dispatch for complex gcd. + (do_extended_gcd): Ditto. + (Fgcd): Mention that complex gcd is now also possible. + 2010-09-30 Jordi Gutiérrez Hermoso <jordigh@gmail.com> * DLD-FUNCTIONS/gcd.cc (extended_gcd): Fix bug that didn't
--- a/src/DLD-FUNCTIONS/gcd.cc +++ b/src/DLD-FUNCTIONS/gcd.cc @@ -1,7 +1,7 @@ /* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 David Bateman -Copyirght (C) 2010 Jaroslav Hajek +Copyright (C) 2010 Jaroslav Hajek, Jordi Gutiérrez Hermoso This file is part of Octave. @@ -36,7 +36,7 @@ #include "error.h" #include "oct-obj.h" -static double +static double simple_gcd (double a, double b) { if (! xisinteger (a) || ! xisinteger (b)) @@ -53,6 +53,47 @@ 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 = floor((a/b).real() + 0.5); + FP qi = 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, 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) @@ -67,7 +108,7 @@ return aa; } -static double +static double extended_gcd (double a, double b, double& x, double& y) { if (! xisinteger (a) || ! xisinteger (b)) @@ -89,12 +130,54 @@ ly = yy; yy = ty; } - x = a >= 0 ? lx : -lx; + 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, 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, @@ -178,8 +261,11 @@ MAKE_INT_BRANCH (uint64); #undef MAKE_INT_BRANCH case btyp_complex: + retval = do_simple_gcd<ComplexNDArray> (a,b); + break; case btyp_float_complex: - error ("gcd: complex numbers not allowed"); + 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 ()); @@ -275,8 +361,11 @@ 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: - error ("gcd: complex numbers not allowed"); + 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 ()); @@ -309,7 +398,10 @@ 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 integers. For example,\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. For example,\n\ \n\ @noindent\n\ and\n\ @@ -380,6 +472,7 @@ %!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 <Invalid call to gcd.*> gcd ();