diff --git a/src/CalcManager/CEngine/Rational.cpp b/src/CalcManager/CEngine/Rational.cpp index fd86b72a..865ff09c 100644 --- a/src/CalcManager/CEngine/Rational.cpp +++ b/src/CalcManager/CEngine/Rational.cpp @@ -189,7 +189,7 @@ namespace CalcEngine try { - modrat(&lhsRat, rhsRat); + remrat(&lhsRat, rhsRat); destroyrat(rhsRat); } catch (uint32_t error) diff --git a/src/CalcManager/CEngine/RationalMath.cpp b/src/CalcManager/CEngine/RationalMath.cpp index 37dc7926..a3cffb7a 100644 --- a/src/CalcManager/CEngine/RationalMath.cpp +++ b/src/CalcManager/CEngine/RationalMath.cpp @@ -387,3 +387,26 @@ Rational RationalMath::ATanh(Rational const& rat) return result; } + + +Rational RationalMath::Mod(Rational const& base, Rational const& n) +{ + PRAT prat = base.ToPRAT(); + PRAT pn = n.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..22dabdba 100644 --- a/src/CalcManager/CEngine/scioper.cpp +++ b/src/CalcManager/CEngine/scioper.cpp @@ -112,8 +112,17 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa } else { - iFinalSign = iNumeratorSign; - result %= temp; + if (m_fIntegerMode) + { + // Programmer mode + iFinalSign = iNumeratorSign; + result %= temp; + } + else + { + iFinalSign = iDenominatorSign; + result = Mod(result, temp); + } } if (m_fIntegerMode && iFinalSign == -1) diff --git a/src/CalcManager/Header Files/RationalMath.h b/src/CalcManager/Header Files/RationalMath.h index b52c1c5f..7a9f86ba 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& base, Rational const& n); 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..fa80b760 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. //----------------------------------------------------------------------------- @@ -96,7 +96,7 @@ void asinrat( PRAT *px, uint32_t radix, int32_t precision) PRAT pret= nullptr; PRAT phack= nullptr; - sgn = (*px)->pp->sign* (*px)->pq->sign; + sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; @@ -206,7 +206,7 @@ void acosrat( PRAT *px, uint32_t radix, int32_t precision) { int32_t sgn; - sgn = (*px)->pp->sign*(*px)->pq->sign; + sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; @@ -294,7 +294,7 @@ void atanrat( PRAT *px, uint32_t radix, int32_t precision) int32_t sgn; PRAT tmpx= nullptr; - sgn = (*px)->pp->sign * (*px)->pq->sign; + 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..41622b51 100644 --- a/src/CalcManager/Ratpack/logic.cpp +++ b/src/CalcManager/Ratpack/logic.cpp @@ -183,6 +183,41 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func ) *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; +// NOTE: 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); +} + //----------------------------------------------------------------------------- // // FUNCTION: modrat @@ -191,28 +226,36 @@ 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, equivalent of 'pa modulo b' in arithmetic +// NOTE: produces a result that is either zero or has the same sign as the divisor. // //----------------------------------------------------------------------------- -void modrat( PRAT *pa, PRAT b ) - +void modrat(PRAT *pa, PRAT b) { + //contrary to remrat, modrat(a, 0) must return a + 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) + { + 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/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..229aca96 --- /dev/null +++ b/src/CalculatorUnitTests/RationalTest.cpp @@ -0,0 +1,76 @@ +// 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 RationalTests +{ + TEST_CLASS(RationalTest) + { + public: + TEST_CLASS_INITIALIZE(CommonSetup) + { + ChangeConstants(10, 128); + } + TEST_METHOD(ModuloTest) + { + Rational rat25(25); + Rational ratminus25(-25); + Rational rat4(4); + Rational ratminus4(-4); + Rational res = Mod(rat25, rat4); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"1"); + res = Mod(rat25, ratminus4); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-3"); + res = Mod(ratminus25, ratminus4); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-1"); + res = Mod(ratminus25, rat4); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"3"); + + res = Mod(Rational(426), Rational(56478)); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"426"); + res = Mod(Rational(56478), Rational(426)); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"246"); + res = Mod(Rational(-643), Rational(8756)); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"8113"); + res = Mod(Rational(643), Rational(-8756)); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-8113"); + res = Mod(Rational(-643), Rational(-8756)); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-643"); + } + + TEST_METHOD(RemainderTest) + { + Rational rat25(25); + Rational ratminus25(-25); + Rational rat4(4); + Rational ratminus4(-4); + Rational res = rat25 % rat4; + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"1"); + res = rat25 % ratminus4; + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"1"); + res = ratminus25 % ratminus4; + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-1"); + res = ratminus25 % rat4; + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-1"); + + res = Rational(426) % Rational(56478); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"426"); + res = Rational(56478) % Rational(426); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"246"); + res = Rational(-643) % Rational(8756); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-643"); + res = Rational(643) % Rational(-8756); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"643"); + res = Rational(-643) % Rational(-8756); + VERIFY_ARE_EQUAL(res.ToString(10, FMT_FLOAT, 128), L"-643"); + } + }; +}