diff --git a/Makefile.am b/Makefile.am index 814c1e6e..8e0067cb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -334,6 +334,7 @@ TEST_BIN = \ test/pool \ test/rand \ test/range \ + test/rational \ test/region \ test/ripemd \ test/sha1 \ diff --git a/rational.cpp b/rational.cpp index bee06132..9fe513d2 100644 --- a/rational.cpp +++ b/rational.cpp @@ -16,85 +16,151 @@ #include "rational.hpp" +#include "maths.hpp" + #include using util::rational; -//----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// template rational::rational (T _n, T _d): n (_n), d (_d) -{ ; } +{ + if (n < 0 && d < 0) { + n *= -1; + d *= -1; + } +} //----------------------------------------------------------------------------- template +rational::rational (T v): + n (v), + d (1) +{ ; } + + +/////////////////////////////////////////////////////////////////////////////// +template bool -rational::operator== (const rational &rhs) { +rational::operator== (const rational rhs) const +{ return rhs.n == n && rhs.d == d; } //----------------------------------------------------------------------------- template -rational::operator float (void) const { +bool +rational::operator!= (const rational rhs) const +{ + return !operator== (rhs); +} + + +//----------------------------------------------------------------------------- +template +bool +rational::operator< (const rational rhs) const +{ + return n * rhs.d < rhs.n * d; +} + + +//----------------------------------------------------------------------------- +template +bool +rational::operator>= (const rational rhs) const +{ + return n * rhs.d >= rhs.n * d; +} + + +/////////////////////////////////////////////////////////////////////////////// +template +rational::operator float (void) const +{ return static_cast (n) / d; } //----------------------------------------------------------------------------- template -rational::operator double (void) const { +rational::operator double (void) const +{ return static_cast (n) / d; } -//----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// template -rational::operator int (void) const { - return n / d; +rational +rational::reduced (void) const +{ + auto x = gcd (abs (n), abs (d)); + + return { n / x, d / x }; } -//----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// template rational -rational::inverse (void) const { +rational::inverse (void) const +{ return rational { d, n }; } //----------------------------------------------------------------------------- template -template rational -rational::operator* (const U &rhs) const { +rational::operator+ (const T rhs) const +{ + return { n + rhs * d, d }; +} + + +//----------------------------------------------------------------------------- +template +rational +rational::operator- (const T rhs) const +{ + return { n - rhs * d, d }; +} + + +//----------------------------------------------------------------------------- +template +rational +rational::operator* (const T rhs) const +{ return { rhs * n, d }; } //----------------------------------------------------------------------------- template -template rational -rational::operator/ (const U &rhs) const { +rational::operator/ (const T rhs) const +{ return { n, rhs * d }; } //----------------------------------------------------------------------------- -template +template rational -util::operator/ (U lhs, rational rhs) { +util::operator/ (T lhs, rational rhs) { return rhs.inverse () * lhs; } //----------------------------------------------------------------------------- -namespace util { - template struct rational; - template struct rational; +template struct util::rational; +template struct util::rational< int32_t>; - template rational operator/ (int, rational); -} +template util::rational util::operator/ (uint32_t, util::rational); diff --git a/rational.hpp b/rational.hpp index eff6d575..7c30b2cf 100644 --- a/rational.hpp +++ b/rational.hpp @@ -17,30 +17,41 @@ #ifndef __UTIL_RATIONAL_HPP #define __UTIL_RATIONAL_HPP +#include + namespace util { template struct rational { - rational (T _n, T _d); - rational& operator= (const rational&) = default; + static_assert (std::is_integral::value, "only defined for integer types"); - bool operator== (const rational&); + rational (T n, T d); + explicit rational (T); + rational& operator= (const rational&) = default; + + bool operator== (rational) const; + bool operator!= (rational) const; + bool operator< (rational) const; + bool operator>= (rational) const; explicit operator float (void) const; explicit operator double (void) const; - explicit operator int (void) const; + + rational reduced (void) const; rational inverse (void) const; rational& invert (void); - template rational operator* (const U&) const; - template rational operator/ (const U&) const; + rational operator+ (T) const; + rational operator- (T) const; + rational operator* (T) const; + rational operator/ (T) const; T n; T d; }; - template - rational operator/ (U, rational); + template + rational operator/ (T, rational); } #endif diff --git a/test/rational.cpp b/test/rational.cpp new file mode 100644 index 00000000..31740bba --- /dev/null +++ b/test/rational.cpp @@ -0,0 +1,35 @@ +#include "rational.hpp" +#include "tap.hpp" + +using util::rational; + + +int +main (void) +{ + util::TAP::logger tap; + + { + rational val { -3, -2 }; + tap.expect (val.n == 3 && val.d == 2, "reduce double negatives at construction"); + } + + { + rational val { 5 }; + tap.expect (val.n == 5 && val.d == 1, "scalar construction"); + } + + { + rational a { 2, 3 }; + rational b { 3, 2 }; + + tap.expect_eq (a.inverse (), b, "inversion and equality"); + } + + { + rational val { 8, 12 }; + tap.expect_eq (val.reduced (), rational { 2, 3 }, "factorisation"); + } + + return tap.status (); +}