diff --git a/src/CalcManager/CEngine/Rational.cpp b/src/CalcManager/CEngine/Rational.cpp
index fd86b72a..c93c79a6 100644
--- a/src/CalcManager/CEngine/Rational.cpp
+++ b/src/CalcManager/CEngine/Rational.cpp
@@ -182,6 +182,13 @@ namespace CalcEngine
return *this;
}
+ ///
+ /// Calculate the remainder after division, the sign of a result will match the sign of the current object.
+ ///
+ ///
+ /// This function has the same behavior as the standard C/C++ operator '%'
+ /// to calculate the modulus after division instead, use instead.
+ ///
Rational& Rational::operator%=(Rational const& rhs)
{
PRAT lhsRat = this->ToPRAT();
@@ -189,7 +196,7 @@ namespace CalcEngine
try
{
- modrat(&lhsRat, rhsRat);
+ remrat(&lhsRat, rhsRat);
destroyrat(rhsRat);
}
catch (uint32_t error)
@@ -342,6 +349,12 @@ namespace CalcEngine
return lhs;
}
+ ///
+ /// Calculate the remainder after division, the sign of a result will match the sign of lhs.
+ ///
+ ///
+ /// This function has the same behavior as the standard C/C++ operator '%', to calculate the modulus after division instead, use instead.
+ ///
Rational operator%(Rational lhs, Rational const& rhs)
{
lhs %= rhs;
diff --git a/src/CalcManager/CEngine/RationalMath.cpp b/src/CalcManager/CEngine/RationalMath.cpp
index 37dc7926..f5de74fc 100644
--- a/src/CalcManager/CEngine/RationalMath.cpp
+++ b/src/CalcManager/CEngine/RationalMath.cpp
@@ -387,3 +387,33 @@ Rational RationalMath::ATanh(Rational const& rat)
return result;
}
+
+///
+/// Calculate the modulus after division, the sign of the result will match the sign of b.
+///
+///
+/// When one of the operand is negative
+/// the result will differ from the C/C++ operator '%'
+/// use instead to calculate the remainder after division.
+///
+Rational RationalMath::Mod(Rational const& a, Rational const& b)
+{
+ PRAT prat = a.ToPRAT();
+ PRAT pn = b.ToPRAT();
+
+ try
+ {
+ modrat(&prat, pn);
+ destroyrat(pn);
+ }
+ catch (uint32_t error)
+ {
+ destroyrat(prat);
+ destroyrat(pn);
+ throw(error);
+ }
+
+ auto res = Rational{ prat };
+ destroyrat(prat);
+ return res;
+}
diff --git a/src/CalcManager/CEngine/scioper.cpp b/src/CalcManager/CEngine/scioper.cpp
index b09083c4..9ced7021 100644
--- a/src/CalcManager/CEngine/scioper.cpp
+++ b/src/CalcManager/CEngine/scioper.cpp
@@ -78,7 +78,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
case IDC_DIV:
case IDC_MOD:
{
- int iNumeratorSign = 1, iDenominatorSign = 1, iFinalSign = 1;
+ int iNumeratorSign = 1, iDenominatorSign = 1;
auto temp = result;
result = rhs;
@@ -107,20 +107,30 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
if (operation == IDC_DIV)
{
- iFinalSign = iNumeratorSign * iDenominatorSign;
result /= temp;
+ if (m_fIntegerMode && (iNumeratorSign * iDenominatorSign) == -1)
+ {
+ result = -(Integer(result));
+ }
}
else
{
- iFinalSign = iNumeratorSign;
- result %= temp;
- }
+ if (m_fIntegerMode)
+ {
+ // Programmer mode, use remrat (remainder after division)
+ result %= temp;
- if (m_fIntegerMode && iFinalSign == -1)
- {
- result = -(Integer(result));
+ if (iNumeratorSign == -1)
+ {
+ result = -(Integer(result));
+ }
+ }
+ else
+ {
+ //other modes, use modrat (modulus after division)
+ result = Mod(result, temp);
+ }
}
-
break;
}
diff --git a/src/CalcManager/Header Files/CalcEngine.h b/src/CalcManager/Header Files/CalcEngine.h
index db906e51..c84f2d55 100644
--- a/src/CalcManager/Header Files/CalcEngine.h
+++ b/src/CalcManager/Header Files/CalcEngine.h
@@ -45,7 +45,7 @@ namespace CalculationManager
class IResourceProvider;
}
-namespace CalculatorUnitTests
+namespace CalculatorEngineTests
{
class CalcEngineTests;
}
@@ -160,5 +160,5 @@ private:
static void ChangeBaseConstants(uint32_t radix, int maxIntDigits, int32_t precision);
void BaseOrPrecisionChanged();
- friend class CalculatorUnitTests::CalcEngineTests;
+ friend class CalculatorEngineTests::CalcEngineTests;
};
diff --git a/src/CalcManager/Header Files/RationalMath.h b/src/CalcManager/Header Files/RationalMath.h
index b52c1c5f..59500573 100644
--- a/src/CalcManager/Header Files/RationalMath.h
+++ b/src/CalcManager/Header Files/RationalMath.h
@@ -13,6 +13,7 @@ namespace CalcEngine::RationalMath
Rational Pow(Rational const& base, Rational const& pow);
Rational Root(Rational const& base, Rational const& root);
Rational Fact(Rational const& rat);
+ Rational Mod(Rational const& a, Rational const& b);
Rational Exp(Rational const& rat);
Rational Log(Rational const& rat);
diff --git a/src/CalcManager/Ratpack/exp.cpp b/src/CalcManager/Ratpack/exp.cpp
index c8691cfa..95637404 100644
--- a/src/CalcManager/Ratpack/exp.cpp
+++ b/src/CalcManager/Ratpack/exp.cpp
@@ -408,7 +408,7 @@ void powratNumeratorDenominator(PRAT *px, PRAT y, uint32_t radix, int32_t precis
//---------------------------------------------------------------------------
void powratcomp(PRAT *px, PRAT y, uint32_t radix, int32_t precision)
{
- int32_t sign = ((*px)->pp->sign * (*px)->pq->sign);
+ int32_t sign = SIGN(*px);
// Take the absolute value
(*px)->pp->sign = 1;
diff --git a/src/CalcManager/Ratpack/fact.cpp b/src/CalcManager/Ratpack/fact.cpp
index c974b005..276151ae 100644
--- a/src/CalcManager/Ratpack/fact.cpp
+++ b/src/CalcManager/Ratpack/fact.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
@@ -216,7 +216,7 @@ void factrat( PRAT *px, uint32_t radix, int32_t precision)
// Check for negative integers and throw an error.
if ( ( zerrat(frac) || ( LOGRATRADIX(frac) <= -precision) ) &&
- ( (*px)->pp->sign * (*px)->pq->sign == -1 ) )
+ ( SIGN(*px) == -1 ) )
{
throw CALC_E_DOMAIN;
}
diff --git a/src/CalcManager/Ratpack/itrans.cpp b/src/CalcManager/Ratpack/itrans.cpp
index 1ecbf4a1..0d2a297e 100644
--- a/src/CalcManager/Ratpack/itrans.cpp
+++ b/src/CalcManager/Ratpack/itrans.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
@@ -92,11 +92,9 @@ void asinanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32
void asinrat( PRAT *px, uint32_t radix, int32_t precision)
{
- int32_t sgn;
PRAT pret= nullptr;
PRAT phack= nullptr;
-
- sgn = (*px)->pp->sign* (*px)->pq->sign;
+ int32_t sgn = SIGN(*px);
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
@@ -204,9 +202,7 @@ void _acosrat( PRAT *px, int32_t precision)
void acosrat( PRAT *px, uint32_t radix, int32_t precision)
{
- int32_t sgn;
-
- sgn = (*px)->pp->sign*(*px)->pq->sign;
+ int32_t sgn = SIGN(*px);
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
@@ -291,10 +287,8 @@ void _atanrat( PRAT *px, int32_t precision)
void atanrat( PRAT *px, uint32_t radix, int32_t precision)
{
- int32_t sgn;
PRAT tmpx= nullptr;
-
- sgn = (*px)->pp->sign * (*px)->pq->sign;
+ int32_t sgn = SIGN(*px);
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
diff --git a/src/CalcManager/Ratpack/logic.cpp b/src/CalcManager/Ratpack/logic.cpp
index c7f8a3a3..6444898e 100644
--- a/src/CalcManager/Ratpack/logic.cpp
+++ b/src/CalcManager/Ratpack/logic.cpp
@@ -18,54 +18,54 @@
using namespace std;
-void lshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
+void lshrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
- PRAT pwr= nullptr;
+ PRAT pwr = nullptr;
int32_t intb;
intrat(pa, radix, precision);
- if ( !zernum( (*pa)->pp ) )
- {
+ if (!zernum((*pa)->pp))
+ {
// If input is zero we're done.
- if ( rat_gt( b, rat_max_exp, precision) )
- {
+ if (rat_gt(b, rat_max_exp, precision))
+ {
// Don't attempt lsh of anything big
- throw( CALC_E_DOMAIN );
- }
+ throw(CALC_E_DOMAIN);
+ }
intb = rattoi32(b, radix, precision);
- DUPRAT(pwr,rat_two);
+ DUPRAT(pwr, rat_two);
ratpowi32(&pwr, intb, precision);
mulrat(pa, pwr, precision);
destroyrat(pwr);
- }
+ }
}
-void rshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
+void rshrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
- PRAT pwr= nullptr;
+ PRAT pwr = nullptr;
int32_t intb;
intrat(pa, radix, precision);
- if ( !zernum( (*pa)->pp ) )
- {
+ if (!zernum((*pa)->pp))
+ {
// If input is zero we're done.
- if ( rat_lt( b, rat_min_exp, precision) )
- {
+ if (rat_lt(b, rat_min_exp, precision))
+ {
// Don't attempt rsh of anything big and negative.
- throw( CALC_E_DOMAIN );
- }
+ throw(CALC_E_DOMAIN);
+ }
intb = rattoi32(b, radix, precision);
- DUPRAT(pwr,rat_two);
+ DUPRAT(pwr, rat_two);
ratpowi32(&pwr, intb, precision);
divrat(pa, pwr, precision);
destroyrat(pwr);
- }
+ }
}
-void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision);
-void boolnum( PNUMBER *pa, PNUMBER b, int func );
+void boolrat(PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision);
+void boolnum(PNUMBER *pa, PNUMBER b, int func);
enum {
@@ -74,22 +74,22 @@ enum {
FUNC_XOR
} BOOL_FUNCS;
-void andrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
+void andrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
- boolrat( pa, b, FUNC_AND, radix, precision);
+ boolrat(pa, b, FUNC_AND, radix, precision);
}
-void orrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
+void orrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
- boolrat( pa, b, FUNC_OR, radix, precision);
+ boolrat(pa, b, FUNC_OR, radix, precision);
}
-void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
+void xorrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
- boolrat( pa, b, FUNC_XOR, radix, precision);
+ boolrat(pa, b, FUNC_XOR, radix, precision);
}
//---------------------------------------------------------------------------
@@ -104,15 +104,15 @@ void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
//
//---------------------------------------------------------------------------
-void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision)
+void boolrat(PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision)
{
- PRAT tmp= nullptr;
- intrat( pa, radix, precision);
- DUPRAT(tmp,b);
- intrat( &tmp, radix, precision);
+ PRAT tmp = nullptr;
+ intrat(pa, radix, precision);
+ DUPRAT(tmp, b);
+ intrat(&tmp, radix, precision);
- boolnum( &((*pa)->pp), tmp->pp, func );
+ boolnum(&((*pa)->pp), tmp->pp, func);
destroyrat(tmp);
}
@@ -130,11 +130,11 @@ void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision)
//
//---------------------------------------------------------------------------
-void boolnum( PNUMBER *pa, PNUMBER b, int func )
+void boolnum(PNUMBER *pa, PNUMBER b, int func)
{
- PNUMBER c= nullptr;
- PNUMBER a= nullptr;
+ PNUMBER c = nullptr;
+ PNUMBER a = nullptr;
MANTTYPE *pcha;
MANTTYPE *pchb;
MANTTYPE *pchc;
@@ -143,26 +143,26 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func )
MANTTYPE da;
MANTTYPE db;
- a=*pa;
- cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) -
- min( a->exp, b->exp );
- createnum( c, cdigits );
- c->exp = min( a->exp, b->exp );
+ a = *pa;
+ cdigits = max(a->cdigit + a->exp, b->cdigit + b->exp) -
+ min(a->exp, b->exp);
+ createnum(c, cdigits);
+ c->exp = min(a->exp, b->exp);
mexp = c->exp;
c->cdigit = cdigits;
pcha = a->mant;
pchb = b->mant;
pchc = c->mant;
- for ( ;cdigits > 0; cdigits--, mexp++ )
+ for (; cdigits > 0; cdigits--, mexp++)
+ {
+ da = (((mexp >= a->exp) && (cdigits + a->exp - c->exp >
+ (c->cdigit - a->cdigit))) ?
+ *pcha++ : 0);
+ db = (((mexp >= b->exp) && (cdigits + b->exp - c->exp >
+ (c->cdigit - b->cdigit))) ?
+ *pchb++ : 0);
+ switch (func)
{
- da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp >
- (c->cdigit - a->cdigit) ) ) ?
- *pcha++ : 0 );
- db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp >
- (c->cdigit - b->cdigit) ) ) ?
- *pchb++ : 0 );
- switch ( func )
- {
case FUNC_AND:
*pchc++ = da & db;
break;
@@ -172,15 +172,51 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func )
case FUNC_XOR:
*pchc++ = da ^ db;
break;
- }
}
+ }
c->sign = a->sign;
- while ( c->cdigit > 1 && *(--pchc) == 0 )
- {
+ while (c->cdigit > 1 && *(--pchc) == 0)
+ {
c->cdigit--;
- }
- destroynum( *pa );
- *pa=c;
+ }
+ destroynum(*pa);
+ *pa = c;
+}
+
+//-----------------------------------------------------------------------------
+//
+// FUNCTION: remrat
+//
+// ARGUMENTS: pointer to a rational a second rational.
+//
+// RETURN: None, changes pointer.
+//
+// DESCRIPTION: Calculate the remainder of *pa / b,
+// equivalent of 'pa % b' in C/C++ and produces a result
+// that is either zero or has the same sign as the dividend.
+//
+//-----------------------------------------------------------------------------
+
+void remrat(PRAT *pa, PRAT b)
+
+{
+ if (zerrat(b))
+ {
+ throw CALC_E_INDEFINITE;
+ }
+
+ PRAT tmp = nullptr;
+ DUPRAT(tmp, b);
+
+ mulnumx(&((*pa)->pp), tmp->pq);
+ mulnumx(&(tmp->pp), (*pa)->pq);
+ remnum(&((*pa)->pp), tmp->pp, BASEX);
+ mulnumx(&((*pa)->pq), tmp->pq);
+
+ // Get *pa back in the integer over integer form.
+ RENORMALIZE(*pa);
+
+ destroyrat(tmp);
}
//-----------------------------------------------------------------------------
@@ -191,28 +227,38 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func )
//
// RETURN: None, changes pointer.
//
-// DESCRIPTION: Does the rational equivalent of frac(*pa);
+// DESCRIPTION: Calculate the remainder of *pa / b, with the sign of the result
+// either zero or has the same sign as the divisor.
+// NOTE: When *pa or b are negative, the result won't be the same as
+// the C/C++ operator %, use remrat if it's the behavior you expect.
//
//-----------------------------------------------------------------------------
-void modrat( PRAT *pa, PRAT b )
-
+void modrat(PRAT *pa, PRAT b)
{
+ //contrary to remrat(X, 0) returning 0, modrat(X, 0) must return X
+ if (zerrat(b))
+ {
+ return;
+ }
+
PRAT tmp = nullptr;
+ DUPRAT(tmp, b);
- if ( zerrat( b ) )
- {
- throw CALC_E_INDEFINITE;
- }
- DUPRAT(tmp,b);
+ auto needAdjust = (SIGN(*pa) == -1 ? (SIGN(b) == 1) : (SIGN(b) == -1));
- mulnumx( &((*pa)->pp), tmp->pq );
- mulnumx( &(tmp->pp), (*pa)->pq );
- remnum( &((*pa)->pp), tmp->pp, BASEX );
- mulnumx( &((*pa)->pq), tmp->pq );
+ mulnumx(&((*pa)->pp), tmp->pq);
+ mulnumx(&(tmp->pp), (*pa)->pq);
+ remnum(&((*pa)->pp), tmp->pp, BASEX);
+ mulnumx(&((*pa)->pq), tmp->pq);
+
+ if (needAdjust && !zerrat(*pa))
+ {
+ addrat(pa, b, BASEX);
+ }
// Get *pa back in the integer over integer form.
RENORMALIZE(*pa);
- destroyrat( tmp );
+ destroyrat(tmp);
}
diff --git a/src/CalcManager/Ratpack/ratpak.h b/src/CalcManager/Ratpack/ratpak.h
index 75ac28de..d9bc11ce 100644
--- a/src/CalcManager/Ratpack/ratpak.h
+++ b/src/CalcManager/Ratpack/ratpak.h
@@ -148,6 +148,9 @@ extern PRAT rat_min_i32;
#define LOGNUM2(pnum) ((pnum)->cdigit+(pnum)->exp)
#define LOGRAT2(prat) (LOGNUM2((prat)->pp)-LOGNUM2((prat)->pq))
+// SIGN returns the sign of the rational
+#define SIGN(prat) ((prat)->pp->sign*(prat)->pq->sign)
+
#if defined( DEBUG_RATPAK )
//-----------------------------------------------------------------------------
//
@@ -423,7 +426,8 @@ extern void divnumx( _Inout_ PNUMBER *pa, _In_ PNUMBER b, int32_t precision);
extern void divrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision);
extern void fracrat( _Inout_ PRAT *pa , uint32_t radix, int32_t precision);
extern void factrat( _Inout_ PRAT *pa, uint32_t radix, int32_t precision);
-extern void modrat( _Inout_ PRAT *pa, _In_ PRAT b );
+extern void remrat(_Inout_ PRAT *pa, _In_ PRAT b);
+extern void modrat(_Inout_ PRAT *pa, _In_ PRAT b);
extern void gcdrat( _Inout_ PRAT *pa, int32_t precision);
extern void intrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
extern void mulnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix);
diff --git a/src/CalcManager/Ratpack/support.cpp b/src/CalcManager/Ratpack/support.cpp
index ebee00c6..22683b4c 100644
--- a/src/CalcManager/Ratpack/support.cpp
+++ b/src/CalcManager/Ratpack/support.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//----------------------------------------------------------------------------
@@ -296,7 +296,7 @@ void intrat( PRAT *px, uint32_t radix, int32_t precision)
// Subtract the fractional part of the rational
PRAT pret = nullptr;
DUPRAT(pret,*px);
- modrat( &pret, rat_one );
+ remrat( &pret, rat_one );
subrat( px, pret, precision);
destroyrat( pret );
@@ -348,8 +348,7 @@ bool rat_ge( PRAT a, PRAT b, int32_t precision)
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
- bool bret = ( zernum( rattmp->pp ) ||
- rattmp->pp->sign * rattmp->pq->sign == 1 );
+ bool bret = ( zernum( rattmp->pp ) || SIGN(rattmp) == 1 );
destroyrat( rattmp );
return( bret );
}
@@ -374,8 +373,7 @@ bool rat_gt( PRAT a, PRAT b, int32_t precision)
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
- bool bret = ( !zernum( rattmp->pp ) &&
- rattmp->pp->sign * rattmp->pq->sign == 1 );
+ bool bret = ( !zernum( rattmp->pp ) && SIGN(rattmp) == 1 );
destroyrat( rattmp );
return( bret );
}
@@ -400,8 +398,7 @@ bool rat_le( PRAT a, PRAT b, int32_t precision)
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
- bool bret = ( zernum( rattmp->pp ) ||
- rattmp->pp->sign * rattmp->pq->sign == -1 );
+ bool bret = ( zernum( rattmp->pp ) || SIGN(rattmp) == -1 );
destroyrat( rattmp );
return( bret );
}
@@ -426,8 +423,7 @@ bool rat_lt( PRAT a, PRAT b, int32_t precision)
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
- bool bret = ( !zernum( rattmp->pp ) &&
- rattmp->pp->sign * rattmp->pq->sign == -1 );
+ bool bret = ( !zernum( rattmp->pp ) && SIGN(rattmp) == -1 );
destroyrat( rattmp );
return( bret );
}
diff --git a/src/CalculatorUnitTests/CalcEngineTests.cpp b/src/CalculatorUnitTests/CalcEngineTests.cpp
index d572a442..9109c8d6 100644
--- a/src/CalculatorUnitTests/CalcEngineTests.cpp
+++ b/src/CalculatorUnitTests/CalcEngineTests.cpp
@@ -13,7 +13,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
static constexpr size_t MAX_HISTORY_SIZE = 20;
-namespace CalculatorUnitTests
+namespace CalculatorEngineTests
{
TEST_CLASS(CalcEngineTests)
{
diff --git a/src/CalculatorUnitTests/CalcInputTest.cpp b/src/CalculatorUnitTests/CalcInputTest.cpp
index 551fbd71..4d224a3e 100644
--- a/src/CalculatorUnitTests/CalcInputTest.cpp
+++ b/src/CalculatorUnitTests/CalcInputTest.cpp
@@ -8,7 +8,7 @@ using namespace std;
using namespace CalculationManager;
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
-namespace CalculatorUnitTests
+namespace CalculatorEngineTests
{
TEST_CLASS(CalcInputTest)
{
diff --git a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj
index d199044c..0afdfd14 100644
--- a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj
+++ b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj
@@ -248,6 +248,7 @@
+
diff --git a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters
index 57ac4fe2..20400d4a 100644
--- a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters
+++ b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters
@@ -29,6 +29,7 @@
Mocks
+
diff --git a/src/CalculatorUnitTests/RationalTest.cpp b/src/CalculatorUnitTests/RationalTest.cpp
new file mode 100644
index 00000000..c19692fd
--- /dev/null
+++ b/src/CalculatorUnitTests/RationalTest.cpp
@@ -0,0 +1,225 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#include "pch.h"
+#include
+#include "Header Files/Rational.h"
+#include "Header Files/RationalMath.h"
+
+using namespace CalcEngine;
+using namespace CalcEngine::RationalMath;
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace CalculatorEngineTests
+{
+ TEST_CLASS(RationalTest)
+ {
+ public:
+ TEST_CLASS_INITIALIZE(CommonSetup)
+ {
+ ChangeConstants(10, 128);
+ }
+
+ TEST_METHOD(TestModuloOperandsNotModified)
+ {
+ // Verify results but also check that operands are not modified
+ Rational rat25(25);
+ Rational ratminus25(-25);
+ Rational rat4(4);
+ Rational ratminus4(-4);
+ Rational res = Mod(rat25, rat4);
+ VERIFY_ARE_EQUAL(res, 1);
+ VERIFY_ARE_EQUAL(rat25, 25);
+ VERIFY_ARE_EQUAL(rat4, 4);
+ res = Mod(rat25, ratminus4);
+ VERIFY_ARE_EQUAL(res, -3);
+ VERIFY_ARE_EQUAL(rat25, 25);
+ VERIFY_ARE_EQUAL(ratminus4, -4);
+ res = Mod(ratminus25, ratminus4);
+ VERIFY_ARE_EQUAL(res, -1);
+ VERIFY_ARE_EQUAL(ratminus25, -25);
+ VERIFY_ARE_EQUAL(ratminus4, -4);
+ res = Mod(ratminus25, rat4);
+ VERIFY_ARE_EQUAL(res, 3);
+ VERIFY_ARE_EQUAL(ratminus25, -25);
+ VERIFY_ARE_EQUAL(rat4, 4);
+ }
+
+ TEST_METHOD(TestModuloInteger)
+ {
+ // Check with integers
+ auto res = Mod(Rational(426), Rational(56478));
+ VERIFY_ARE_EQUAL(res, 426);
+ res = Mod(Rational(56478), Rational(426));
+ VERIFY_ARE_EQUAL(res, 246);
+ res = Mod(Rational(-643), Rational(8756));
+ VERIFY_ARE_EQUAL(res, 8113);
+ res = Mod(Rational(643), Rational(-8756));
+ VERIFY_ARE_EQUAL(res, -8113);
+ res = Mod(Rational(-643), Rational(-8756));
+ VERIFY_ARE_EQUAL(res, -643);
+ res = Mod(Rational(1000), Rational(250));
+ VERIFY_ARE_EQUAL(res, 0);
+ res = Mod(Rational(1000), Rational(-250));
+ VERIFY_ARE_EQUAL(res, 0);
+ }
+
+ TEST_METHOD(TestModuloZero)
+ {
+ // Test with Zero
+ auto res = Mod(Rational(343654332), Rational(0));
+ VERIFY_ARE_EQUAL(res, 343654332);
+ res = Mod(Rational(0), Rational(8756));
+ VERIFY_ARE_EQUAL(res, 0);
+ res = Mod(Rational(0), Rational(-242));
+ VERIFY_ARE_EQUAL(res, 0);
+ res = Mod(Rational(0), Rational(0));
+ VERIFY_ARE_EQUAL(res, 0);
+ res = Mod(Rational(Number(1, 0, { 23242 }), Number(1, 0, { 2 })), Rational(Number(1, 0, { 0 }), Number(1, 0, { 23 })));
+ VERIFY_ARE_EQUAL(res, 11621);
+ }
+
+ TEST_METHOD(TestModuloRational)
+ {
+ // Test with rational numbers
+ auto res = Mod(Rational(Number(1, 0, { 250 }), Number(1, 0, { 100 })), Rational(89));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"2.5");
+ res = Mod(Rational(Number(1, 0, { 3330 }), Number(1, 0, { 1332 })), Rational(1));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"0.5");
+ res = Mod(Rational(Number(1, 0, { 12250 }), Number(1, 0, { 100 })), Rational(10));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"2.5");
+ res = Mod(Rational(Number(-1, 0, { 12250 }), Number(1, 0, { 100 })), Rational(10));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"7.5");
+ res = Mod(Rational(Number(-1, 0, { 12250 }), Number(1, 0, { 100 })), Rational(-10));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-2.5");
+ res = Mod(Rational(Number(1, 0, { 12250 }), Number(1, 0, { 100 })), Rational(-10));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-7.5");
+ res = Mod(Rational(Number(1, 0, { 1000 }), Number(1, 0, { 3 })), Rational(1));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"0.33333333");
+ res = Mod(Rational(Number(1, 0, { 1000 }), Number(1, 0, { 3 })), Rational(-10));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-6.6666667");
+ res = Mod(Rational(834345), Rational(Number(1, 0, { 103 }), Number(1, 0, { 100 })));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"0.71");
+ res = Mod(Rational(834345), Rational(Number(-1, 0, { 103 }), Number(1, 0, { 100 })));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-0.32");
+ }
+
+ TEST_METHOD(TestRemainderOperandsNotModified)
+ {
+ //Verify results but also check that operands are not modified
+ Rational rat25(25);
+ Rational ratminus25(-25);
+ Rational rat4(4);
+ Rational ratminus4(-4);
+ Rational res = rat25 % rat4;
+ VERIFY_ARE_EQUAL(res, 1);
+ VERIFY_ARE_EQUAL(rat25, 25);
+ VERIFY_ARE_EQUAL(rat4, 4);
+ res = rat25 % ratminus4;
+ VERIFY_ARE_EQUAL(res, 1);
+ VERIFY_ARE_EQUAL(rat25, 25);
+ VERIFY_ARE_EQUAL(ratminus4, -4);
+ res = ratminus25 % ratminus4;
+ VERIFY_ARE_EQUAL(res, -1);
+ VERIFY_ARE_EQUAL(ratminus25, -25);
+ VERIFY_ARE_EQUAL(ratminus4, -4);
+ res = ratminus25 % rat4;
+ VERIFY_ARE_EQUAL(res, -1);
+ VERIFY_ARE_EQUAL(ratminus25, -25);
+ VERIFY_ARE_EQUAL(rat4, 4);
+ }
+
+ TEST_METHOD(TestRemainderInteger)
+ {
+ // Check with integers
+ auto res = Rational(426) % Rational(56478);
+ VERIFY_ARE_EQUAL(res, 426);
+ res = Rational(56478) % Rational(426);
+ VERIFY_ARE_EQUAL(res, 246);
+ res = Rational(-643) % Rational(8756);
+ VERIFY_ARE_EQUAL(res, -643);
+ res = Rational(643) % Rational(-8756);
+ VERIFY_ARE_EQUAL(res, 643);
+ res = Rational(-643) % Rational(-8756);
+ VERIFY_ARE_EQUAL(res, -643);
+ res = Rational(-124) % Rational(-124);
+ VERIFY_ARE_EQUAL(res, 0);
+ res = Rational(24) % Rational(24);
+ VERIFY_ARE_EQUAL(res, 0);
+ }
+
+ TEST_METHOD(TestRemainderZero)
+ {
+ // Test with Zero
+ auto res = Rational(0) % Rational(3654);
+ VERIFY_ARE_EQUAL(res, 0);
+ res = Rational(0) % Rational(-242);
+ VERIFY_ARE_EQUAL(res, 0);
+ for (auto number : { 343654332, 0, -23423 })
+ {
+ try
+ {
+ res = Rational(number) % Rational(0);
+ Assert::Fail();
+ }
+ catch (uint32_t t)
+ {
+ if (t != CALC_E_INDEFINITE)
+ {
+ Assert::Fail();
+ }
+ }
+ catch (...)
+ {
+ Assert::Fail();
+ }
+
+ try
+ {
+ res = Rational(Number(1, number, { 0 }), Number(1, 0, { 2 })) % Rational(Number(1, 0, { 0 }), Number(1, 0, { 23 }));
+ Assert::Fail();
+ }
+ catch (uint32_t t)
+ {
+ if (t != CALC_E_INDEFINITE)
+ {
+ Assert::Fail();
+ }
+ }
+ catch (...)
+ {
+ Assert::Fail();
+ }
+ }
+ }
+
+ TEST_METHOD(TestRemainderRational)
+ {
+ //Test with rational numbers
+ auto res = Rational(Number(1, 0, { 250 }), Number(1, 0, { 100 })) % Rational(89);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"2.5");
+ res = Rational(Number(1, 0, { 3330 }), Number(1, 0, { 1332 })) % Rational(1);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"0.5");
+ res = Rational(Number(1, 0, { 12250 }), Number(1, 0, { 100 })) % Rational(10);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"2.5");
+ res = Rational(Number(-1, 0, { 12250 }), Number(1, 0, { 100 })) % Rational(10);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-2.5");
+ res = Rational(Number(-1, 0, { 12250 }), Number(1, 0, { 100 })) % Rational(-10);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-2.5");
+ res = Rational(Number(1, 0, { 12250 }), Number(1, 0, { 100 })) % Rational(-10);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"2.5");
+ res = Rational(Number(1, 0, { 1000 }), Number(1, 0, { 3 })) % Rational(1);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"0.33333333");
+ res = Rational(Number(1, 0, { 1000 }), Number(1, 0, { 3 })) % Rational(-10);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"3.3333333");
+ res = Rational(Number(-1, 0, { 1000 }), Number(1, 0, { 3 })) % Rational(-10);
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-3.3333333");
+ res = Rational(834345) % Rational(Number(1, 0, { 103 }), Number(1, 0, { 100 }));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"0.71");
+ res = Rational(834345) % Rational(Number(-1, 0, { 103 }), Number(1, 0, { 100 }));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"0.71");
+ res = Rational(-834345) % Rational(Number(1, 0, { 103 }), Number(1, 0, { 100 }));
+ VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 8), L"-0.71");
+ }
+ };
+}