From: Daniel Karbach Date: Thu, 21 Feb 2013 07:10:47 +0000 (+0100) Subject: added Rational template (for fractions) X-Git-Url: http://git.localhorst.tv/?p=l2e.git;a=commitdiff_plain;h=7df7c6eca83c2b5c0ee17c55340d8863f9d638f5 added Rational template (for fractions) --- diff --git a/src/math/Rational.h b/src/math/Rational.h new file mode 100644 index 0000000..f4b6620 --- /dev/null +++ b/src/math/Rational.h @@ -0,0 +1,379 @@ +#ifndef MATH_RATIONAL_H_ +#define MATH_RATIONAL_H_ + +#include + + +namespace math { + +/// Rational number type. +/// Stores 2 Integers and has the value num / denom. +template +class Rational { + +public: + explicit Rational(Integer num = 0, Integer denom = 1) + : num(num), denom(denom) { } + template + explicit Rational(const Rational &other) + : num(other.Numerator()), denom(other.Denominator()) { } + +public: + Integer &Numerator() { return num; } + const Integer &Numerator() const { return num; } + Integer &Denominator() { return denom; } + const Integer &Denominator() const { return denom; } + + Integer Int() const { return num / denom; } + Integer Inv() const { return denom / num; } + float Float() const { return float(num) / denom; } + double Double() const { return double(num) / denom; } + + bool IsWhole() const { + return num % denom == 0; + } + bool IsFrac() const { + return num % denom != 0; + } + + void QuickPack() { + if (num == 1 || denom == 1) return; + if (num % denom == 0) { + num /= denom; + denom = 1; + } else if (denom % num == 0) { + denom /= num; + num = 1; + } + } + + void Pack() { + if (num == 1 || denom == 1) return; + if (num % denom == 0) { + num /= denom; + denom = 1; + return; + } + if (denom % num == 0) { + denom /= num; + num = 1; + return; + } + while (num % 2 == 0 && denom % 2 == 0) { + num /= 2; + denom /= 2; + } + while (num % 3 == 0 && denom % 3 == 0) { + num /= 3; + denom /= 3; + } + while (num % 5 == 0 && denom % 5 == 0) { + num /= 5; + denom /= 5; + } + } + +public: + template + Rational &operator =(OtherInt i) { + num = i; + denom = 1; + return *this; + } + template + Rational &operator =(Rational r) { + num = r.Numerator(); + denom = r.Denominator(); + return *this; + } + template + Rational &operator +=(OtherInt i) { + num += i * denom; + return *this; + } + template + Rational &operator +=(Rational r) { + num = num * r.Denominator() + r.Numerator() * denom; + denom *= r.Denominator(); + return *this; + } + template + Rational &operator -=(OtherInt i) { + num -= i * denom; + return *this; + } + template + Rational &operator -=(Rational r) { + num = num * r.Denominator() - r.Numerator() * denom; + denom *= r.Denominator(); + return *this; + } + template + Rational &operator *=(OtherInt i) { + if (denom % i == 0) { + denom /= i; + } else { + num *= i; + } + return *this; + } + template + Rational &operator *=(const Rational &r) { + num *= r.Numerator(); + denom *= r.Denominator(); + return *this; + } + Rational &operator *=(float f) { + num *= f; + return *this; + } + Rational &operator *=(double d) { + num *= d; + return *this; + } + template + Rational &operator /=(OtherInt i) { + if (num % i == 0) { + num /= i; + } else { + denom *= i; + } + return *this; + } + template + Rational &operator /=(const Rational &r) { + num *= r.Denominator(); + denom *= r.Numerator(); + return *this; + } + Rational &operator /=(float f) { + if (f > 1 || f < -1) { + denom *= f; + } else { + num /= f; + } + return *this; + } + Rational &operator /=(double d) { + if (d > 1 || d < -1) { + denom *= d; + } else { + num /= d; + } + return *this; + } + +private: + Integer num; + Integer denom; + +}; + +template +Rational operator -(const Rational &r) { + return Rational(-r.Numerator(), r.Denominator()); +} + +template +bool operator ==( + const Rational &lhs, + const Rational &rhs) { + return lhs.Numerator() * rhs.Denominator() == + rhs.Numerator() * lhs.Denominator(); +} +template +bool operator !=( + const Rational &lhs, + const Rational &rhs) { + return !(lhs == rhs); +} +template +bool operator ==( + const Rational &lhs, + RInt rhs) { + return lhs.IsWhole() && lhs.Int() == rhs; +} +template +bool operator !=( + const Rational &lhs, + RInt rhs) { + return !(lhs == rhs); +} +template +bool operator ==( + LInt lhs, + const Rational &rhs) { + return rhs == lhs; +} +template +bool operator !=( + LInt lhs, + const Rational &rhs) { + return rhs != lhs; +} + +template +bool operator <( + const Rational &lhs, + const Rational &rhs) { + return lhs.Numerator() * rhs.Denominator() < + rhs.Numerator() * lhs.Denominator(); +} +template +bool operator <( + const Rational &lhs, + RInt rhs) { + return lhs.Numerator() < rhs * lhs.Denominator(); +} +template +bool operator <( + LInt lhs, + const Rational &rhs) { + return lhs * rhs.Denominator() < rhs.Numerator(); +} + +template +bool operator <=( + const Rational &lhs, + const Rational &rhs) { + return lhs.Numerator() * rhs.Denominator() <= + rhs.Numerator() * lhs.Denominator(); +} +template +bool operator <=( + const Rational &lhs, + RInt rhs) { + return lhs.Numerator() <= rhs * lhs.Denominator(); +} +template +bool operator <=( + LInt lhs, + const Rational &rhs) { + return lhs * rhs.Denominator() <= rhs.Numerator(); +} + +template +bool operator >( + const Rational &lhs, + const Rational &rhs) { + return lhs.Numerator() * rhs.Denominator() > + rhs.Numerator() * lhs.Denominator(); +} +template +bool operator >( + const Rational &lhs, + RInt rhs) { + return lhs.Numerator() > rhs * lhs.Denominator(); +} +template +bool operator >( + LInt lhs, + const Rational &rhs) { + return lhs * rhs.Denominator() > rhs.Numerator(); +} + +template +bool operator >=( + const Rational &lhs, + const Rational &rhs) { + return lhs.Numerator() * rhs.Denominator() >= + rhs.Numerator() * lhs.Denominator(); +} +template +bool operator >=( + const Rational &lhs, + RInt rhs) { + return lhs.Numerator() >= rhs * lhs.Denominator(); +} +template +bool operator >=( + LInt lhs, + const Rational &rhs) { + return lhs * rhs.Denominator() >= rhs.Numerator(); +} + +template +Rational operator +( + const Rational &lhs, + const Rational &rhs) { + return Rational(lhs) += rhs; +} +template +Rational operator +( + const Rational &lhs, + RInt rhs) { + return Rational(lhs) += rhs; +} +template +LInt operator +( + LInt lhs, + const Rational &rhs) { + return lhs + rhs.Int(); +} + +template +Rational operator -( + const Rational &lhs, + const Rational &rhs) { + return Rational(lhs) -= rhs; +} +template +Rational operator -( + const Rational &lhs, + RInt rhs) { + return Rational(lhs) -= rhs; +} +template +LInt operator -( + LInt lhs, + const Rational &rhs) { + return lhs - rhs.Int(); +} + +template +Rational operator *( + const Rational &lhs, + const Rational &rhs) { + return Rational(lhs) *= rhs; +} +template +Rational operator *( + const Rational &lhs, + RInt rhs) { + return Rational(lhs) *= rhs; +} +template +LInt operator *( + LInt lhs, + const Rational &rhs) { + return lhs * rhs.Numerator() / rhs.Denominator(); +} + +template +Rational operator /( + const Rational &lhs, + const Rational &rhs) { + return Rational(lhs) /= rhs; +} +template +Rational operator /( + const Rational &lhs, + RInt rhs) { + return Rational(lhs) /= rhs; +} +template +LInt operator /( + LInt lhs, + const Rational &rhs) { + return lhs * rhs.Denominator() / rhs.Numerator(); +} + + +template +inline std::ostream &operator <<(std::ostream &out, const Rational &r) { + return out << r.Double(); +} + +} + +#endif diff --git a/tests/math/RationalTest.cpp b/tests/math/RationalTest.cpp new file mode 100644 index 0000000..d2a531a --- /dev/null +++ b/tests/math/RationalTest.cpp @@ -0,0 +1,95 @@ +#include "RationalTest.h" + +#include + +CPPUNIT_TEST_SUITE_REGISTRATION(test_math::RationalTest); + +typedef math::Rational Rational; +typedef math::Rational ShortRational; + + +namespace test_math { + +void RationalTest::setUp() { + +} + +void RationalTest::tearDown() { + +} + + +void RationalTest::testConversion() { + CPPUNIT_ASSERT_EQUAL(1, Rational(3, 2).Int()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.75, Rational(3, 4).Double(), std::numeric_limits::epsilon()); + + CPPUNIT_ASSERT_EQUAL(-1, Rational(-3, 2).Int()); + CPPUNIT_ASSERT_EQUAL(Rational(3, 2), Rational(ShortRational(3, 2))); + CPPUNIT_ASSERT_EQUAL(ShortRational(3, 2), ShortRational(Rational(3, 2))); +} + +void RationalTest::testComparison() { + CPPUNIT_ASSERT_EQUAL(Rational(), Rational()); + CPPUNIT_ASSERT_EQUAL(Rational(1), Rational(1)); + CPPUNIT_ASSERT_EQUAL(Rational(-1), Rational(-1)); + + CPPUNIT_ASSERT_EQUAL(Rational(1, 2), Rational(2, 4)); + + CPPUNIT_ASSERT(Rational(0) != Rational(1)); + CPPUNIT_ASSERT(Rational(0) != Rational(-1)); + CPPUNIT_ASSERT(Rational(1, 2) != Rational(-1, 2)); + + CPPUNIT_ASSERT(Rational(0) < Rational(1)); + CPPUNIT_ASSERT(Rational(1, 2) <= Rational(1)); + CPPUNIT_ASSERT(Rational(0) > Rational(-1)); + CPPUNIT_ASSERT(Rational(1) >= Rational(-1, 2)); + + CPPUNIT_ASSERT(Rational() == ShortRational()); + CPPUNIT_ASSERT(ShortRational() == Rational()); + CPPUNIT_ASSERT(Rational(1) == ShortRational(1)); + CPPUNIT_ASSERT(ShortRational(1) == Rational(1)); + CPPUNIT_ASSERT(Rational(-1) == ShortRational(-1)); + CPPUNIT_ASSERT(ShortRational(-1) == Rational(-1)); + + CPPUNIT_ASSERT(Rational(1, 2) == ShortRational(2, 4)); + CPPUNIT_ASSERT(ShortRational(1, 2) == Rational(2, 4)); + + CPPUNIT_ASSERT(Rational(0) != ShortRational(1)); + CPPUNIT_ASSERT(ShortRational(0) != Rational(1)); + CPPUNIT_ASSERT(Rational(0) != ShortRational(-1)); + CPPUNIT_ASSERT(ShortRational(0) != Rational(-1)); + CPPUNIT_ASSERT(Rational(1, 2) != ShortRational(-1, 2)); + CPPUNIT_ASSERT(ShortRational(1, 2) != Rational(-1, 2)); + + CPPUNIT_ASSERT(Rational(0) < ShortRational(1)); + CPPUNIT_ASSERT(ShortRational(0) < Rational(1)); + CPPUNIT_ASSERT(Rational(1, 2) <= ShortRational(1)); + CPPUNIT_ASSERT(ShortRational(1, 2) <= Rational(1)); + CPPUNIT_ASSERT(Rational(0) > ShortRational(-1)); + CPPUNIT_ASSERT(ShortRational(0) > Rational(-1)); + CPPUNIT_ASSERT(Rational(1) >= ShortRational(-1, 2)); + CPPUNIT_ASSERT(ShortRational(1) >= Rational(-1, 2)); +} + +void RationalTest::testSum() { + CPPUNIT_ASSERT_EQUAL( + Rational(3, 4), + Rational(1, 4) + Rational(1, 2)); +} + +void RationalTest::testProduct() { + CPPUNIT_ASSERT_EQUAL( + Rational(8), + Rational(2) * 4); + CPPUNIT_ASSERT_EQUAL( + Rational(4), + Rational(2) / 0.5); + CPPUNIT_ASSERT_EQUAL( + Rational(3), + Rational(15) / Rational(5)); + CPPUNIT_ASSERT_EQUAL( + Rational(3), + Rational(15) / 5); +} + +} diff --git a/tests/math/RationalTest.h b/tests/math/RationalTest.h new file mode 100644 index 0000000..e288160 --- /dev/null +++ b/tests/math/RationalTest.h @@ -0,0 +1,37 @@ +#ifndef TEST_MATH_RATIONALTEST_H_ +#define TEST_MATH_RATIONALTEST_H_ + +#include "../../src/math/Rational.h" + +#include +#include +#include + + +namespace test_math { + +class RationalTest +: public CppUnit::TestFixture { + +CPPUNIT_TEST_SUITE(RationalTest); +CPPUNIT_TEST(testConversion); +CPPUNIT_TEST(testComparison); +CPPUNIT_TEST(testSum); +CPPUNIT_TEST(testProduct); +CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + + void testConversion(); + void testComparison(); + void testSum(); + void testProduct(); + void testModulo(); + +}; + +} + +#endif