diff --git a/Eule/Math.cpp b/Eule/Math.cpp index 6407ebe..984291a 100644 --- a/Eule/Math.cpp +++ b/Eule/Math.cpp @@ -75,5 +75,24 @@ bool Math::RandomChance(const double chance) return Random() <= chance; } +int Math::Mod(const int numerator, const int denominator) +{ + if (denominator == 0) + throw std::logic_error("Divide by zero"); + + // Quick optimizations: + + // -> 0/n is always 0 + if (numerator == 0) + return 0; + + // -> operator% works for a > 0 && b > 0 + if (denominator > 0 && numerator > 0) + return numerator % denominator; + + // Else: generalized formula + return (denominator + (numerator % denominator)) % denominator; +} + std::mt19937 Math::rng; bool Math::isRngInitialized = true; diff --git a/Eule/Math.h b/Eule/Math.h index d8b9ef3..b2d4462 100644 --- a/Eule/Math.h +++ b/Eule/Math.h @@ -1,5 +1,6 @@ #pragma once #include +#include namespace Eule { @@ -26,6 +27,10 @@ namespace Eule //! Compares two double values with a given accuracy [[nodiscard]] static constexpr bool Similar(const double a, const double b, const double epsilon = 0.00001); + //! Will compute the actual modulo of a fraction. The % operator returns bs for n<0. + //! May throw divide-by-zero std::logic_error + [[nodiscard]] static int Mod(const int numerator, const int denominator); + //! Will return a random double between `0` and `1` static double Random(); diff --git a/Test/Math__Mod.cpp b/Test/Math__Mod.cpp new file mode 100644 index 0000000..d2cab21 --- /dev/null +++ b/Test/Math__Mod.cpp @@ -0,0 +1,83 @@ +#include "CppUnitTest.h" +#include "../Eule/Math.h" +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace Eule; + +/** Equivalence classes: +* a -> numerator +* b -> denominator +* -- a > 0 && b > 0 +* -- a < 0 && b > 0 +* -- a > 0 && b < 0 +* -- a < 0 && b < 0 +* +* -- a > 0 && b = 0 +* -- a = 0 && b > 0 +* * -- a < 0 && b = 0 +* -- a = 0 && b < 0 +* -- a = 0 && b = 0 +*/ + +namespace _Math +{ + TEST_CLASS(_Mod) + { + public: + // a > 0 && b > 0 + TEST_METHOD(a_gt_0_and_b_gt_0) + { + Assert::AreEqual(7, Math::Mod(199, 32)); + return; + } + + // a < 0 && b > 0 + TEST_METHOD(a_lt_0_and_b_gt_0) + { + Assert::AreEqual(25, Math::Mod(-199, 32)); + return; + } + + // a > 0 && b < 0 + TEST_METHOD(a_gt_0_and_b_lt_0) + { + Assert::AreEqual(-25, Math::Mod(199, -32)); + return; + } + + // a > 0 && b = 0 + TEST_METHOD(a_gt_0_and_b_eq_0) + { + // Exppect divide-by-zero + Assert::ExpectException([]() { + Assert::AreEqual(0, Math::Mod(199, 0)); + }); + return; + } + + // a = 0 && b > 0 + TEST_METHOD(a_eq_0_and_b_gt_0) + { + Assert::AreEqual(0, Math::Mod(0, 32)); + return; + } + + // a < 0 && b = 0 + TEST_METHOD(a_lt_0_and_b_eq_0) + { + // Exppect divide-by-zero + Assert::ExpectException([]() { + Assert::AreEqual(0, Math::Mod(-199, 0)); + }); + return; + } + + // a = 0 && b < 0 + TEST_METHOD(a_eq_0_and_b_lt_0) + { + Assert::AreEqual(0, Math::Mod(0, -32)); + return; + } + }; +} diff --git a/Test/_Test_Eule.vcxproj b/Test/_Test_Eule.vcxproj index 120fed0..156e4c9 100644 --- a/Test/_Test_Eule.vcxproj +++ b/Test/_Test_Eule.vcxproj @@ -33,6 +33,7 @@ + diff --git a/Test/_Test_Eule.vcxproj.filters b/Test/_Test_Eule.vcxproj.filters index b966449..1368b38 100644 --- a/Test/_Test_Eule.vcxproj.filters +++ b/Test/_Test_Eule.vcxproj.filters @@ -75,5 +75,8 @@ Tests + + Tests\Math + \ No newline at end of file