Update Calc Engine for new functions needed for keyboard refresh (#662)

* Update Calc Engine to Support New Functionality

* Address PR comments

* Address PR comments
This commit is contained in:
Pepe Rivera
2019-09-30 14:04:20 -07:00
committed by GitHub
parent d9bf57ff99
commit 9cb0932eaa
22 changed files with 849 additions and 176 deletions

View File

@@ -261,6 +261,11 @@ void CalcInput::SetDecimalSymbol(wchar_t decSymbol)
}
}
bool CalcInput::IsEmpty()
{
return m_base.IsEmpty() && !m_hasExponent && m_exponent.IsEmpty() && !m_hasDecimal;
}
wstring CalcInput::ToString(uint32_t radix)
{
// In theory both the base and exponent could be C_NUM_MAX_DIGITS long.

View File

@@ -11,14 +11,14 @@ bool IsOpInRange(OpCode op, uint32_t x, uint32_t y)
bool IsBinOpCode(OpCode opCode)
{
return IsOpInRange(opCode, IDC_AND, IDC_PWR);
return IsOpInRange(opCode, IDC_AND, IDC_PWR) || IsOpInRange(opCode, IDC_BINARYEXTENDEDFIRST, IDC_BINARYEXTENDEDLAST);
}
// WARNING: IDC_SIGN is a special unary op but still this doesn't catch this. Caller has to be aware
// of it and catch it themselves or not needing this
bool IsUnaryOpCode(OpCode opCode)
{
return IsOpInRange(opCode, IDC_UNARYFIRST, IDC_UNARYLAST);
return (IsOpInRange(opCode, IDC_UNARYFIRST, IDC_UNARYLAST) || IsOpInRange(opCode, IDC_UNARYEXTENDEDFIRST, IDC_UNARYEXTENDEDLAST));
}
bool IsDigitOpCode(OpCode opCode)

View File

@@ -120,12 +120,12 @@ void CHistoryCollector::RemoveLastOpndFromHistory()
// This will not restore the m_lastBinOpStartIndex, as it isn't possible to remove that also later
}
void CHistoryCollector::AddBinOpToHistory(int nOpCode, bool fNoRepetition)
void CHistoryCollector::AddBinOpToHistory(int nOpCode, bool isIntegerMode, bool fNoRepetition)
{
int iCommandEnd = AddCommand(std::make_shared<CBinaryCommand>(nOpCode));
m_lastBinOpStartIndex = IchAddSzToEquationSz(L" ", -1);
IchAddSzToEquationSz(CCalcEngine::OpCodeToString(nOpCode), iCommandEnd);
IchAddSzToEquationSz(CCalcEngine::OpCodeToBinaryString(nOpCode, isIntegerMode), iCommandEnd);
IchAddSzToEquationSz(L" ", -1);
if (fNoRepetition)
@@ -138,14 +138,14 @@ void CHistoryCollector::AddBinOpToHistory(int nOpCode, bool fNoRepetition)
// This is expected to be called when a binary op in the last say 1+2+ is changing to another one say 1+2* (+ changed to *)
// It needs to know by this change a Precedence inversion happened. i.e. previous op was lower or equal to its previous op, but the new
// one isn't. (Eg. 1*2* to 1*2^). It can add explicit brackets to ensure the precedence is inverted. (Eg. (1*2) ^)
void CHistoryCollector::ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher)
void CHistoryCollector::ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher, bool isIntgerMode)
{
TruncateEquationSzFromIch(m_lastBinOpStartIndex);
if (fPrecInvToHigher)
{
EnclosePrecInversionBrackets();
}
AddBinOpToHistory(nOpCode);
AddBinOpToHistory(nOpCode, isIntgerMode);
}
void CHistoryCollector::PushLastOpndStart(int ichOpndStart)
@@ -266,6 +266,30 @@ void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE a
command = fInv ? static_cast<int>(CalculationManager::Command::CommandATANH) : IDC_TANH;
spExpressionCommand = std::make_shared<CUnaryCommand>(command);
break;
case IDC_SEC:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandASEC) : IDC_SEC;
spExpressionCommand = std::make_shared<CUnaryCommand>(static_cast<int>(angleOpCode), command);
break;
case IDC_CSC:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandACSC) : IDC_CSC;
spExpressionCommand = std::make_shared<CUnaryCommand>(static_cast<int>(angleOpCode), command);
break;
case IDC_COT:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandACOT) : IDC_COT;
spExpressionCommand = std::make_shared<CUnaryCommand>(static_cast<int>(angleOpCode), command);
break;
case IDC_SECH:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandASECH) : IDC_SECH;
spExpressionCommand = std::make_shared<CUnaryCommand>(command);
break;
case IDC_CSCH:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandACSCH) : IDC_CSCH;
spExpressionCommand = std::make_shared<CUnaryCommand>(command);
break;
case IDC_COTH:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandACOTH) : IDC_COTH;
spExpressionCommand = std::make_shared<CUnaryCommand>(command);
break;
case IDC_LN:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandPOWE) : IDC_LN;
spExpressionCommand = std::make_shared<CUnaryCommand>(command);

View File

@@ -16,6 +16,7 @@
#include <string>
#include "Header Files/CalcEngine.h"
#include "Header Files/CalcUtils.h"
#include "NumberFormattingUtils.h"
using namespace std;
using namespace CalcEngine;
@@ -28,8 +29,13 @@ namespace
// 0 is returned. Higher the number, higher the precedence of the operator.
int NPrecedenceOfOp(int nopCode)
{
static uint8_t rgbPrec[] = { 0, 0, IDC_OR, 0, IDC_XOR, 0, IDC_AND, 1, IDC_ADD, 2, IDC_SUB, 2, IDC_RSHF,
3, IDC_LSHF, 3, IDC_MOD, 3, IDC_DIV, 3, IDC_MUL, 3, IDC_PWR, 4, IDC_ROOT, 4 };
static uint16_t rgbPrec[] = {
0,0, IDC_OR,0, IDC_XOR,0,
IDC_AND,1, IDC_NAND,1, IDC_NOR,1,
IDC_ADD,2, IDC_SUB,2,
IDC_RSHF,3, IDC_LSHF,3, IDC_RSHFL,3,
IDC_MOD,3, IDC_DIV,3, IDC_MUL,3,
IDC_PWR,4, IDC_ROOT,4, IDC_LOGBASEX,4 };
unsigned int iPrec;
iPrec = 0;
@@ -124,9 +130,18 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
// Toggle Record/Display mode if appropriate.
if (m_bRecord)
{
if (IsOpInRange(wParam, IDC_AND, IDC_MMINUS) || IsOpInRange(wParam, IDC_OPENP, IDC_CLOSEP) || IsOpInRange(wParam, IDM_HEX, IDM_BIN)
|| IsOpInRange(wParam, IDM_QWORD, IDM_BYTE) || IsOpInRange(wParam, IDM_DEG, IDM_GRAD)
|| IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITSTART + 63) || (IDC_INV == wParam) || (IDC_SIGN == wParam && 10 != m_radix))
if (IsBinOpCode(wParam) ||
IsUnaryOpCode(wParam) ||
IsOpInRange(wParam, IDC_FE, IDC_MMINUS) ||
IsOpInRange(wParam, IDC_OPENP, IDC_CLOSEP) ||
IsOpInRange(wParam, IDM_HEX, IDM_BIN) ||
IsOpInRange(wParam, IDM_QWORD, IDM_BYTE) ||
IsOpInRange(wParam, IDM_DEG, IDM_GRAD) ||
IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITEND) ||
(IDC_INV == wParam) ||
(IDC_SIGN == wParam && 10 != m_radix) ||
(IDC_RAND == wParam) ||
(IDC_EULER == wParam))
{
m_bRecord = false;
m_currentVal = m_input.ToRational(m_radix, m_precision);
@@ -193,7 +208,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
m_nPrevOpCode = 0; // Once the precedence inversion has put additional brackets, its no longer required
}
}
m_HistoryCollector.ChangeLastBinOp(m_nOpCode, fPrecInvToHigher);
m_HistoryCollector.ChangeLastBinOp(m_nOpCode, fPrecInvToHigher, m_fIntegerMode);
DisplayAnnounceBinaryOperator();
return;
}
@@ -270,10 +285,9 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
}
DisplayAnnounceBinaryOperator();
m_lastVal = m_currentVal;
m_nOpCode = (int)wParam;
m_HistoryCollector.AddBinOpToHistory(m_nOpCode);
m_HistoryCollector.AddBinOpToHistory(m_nOpCode, m_fIntegerMode);
m_bNoPrevEqu = m_bChangeOp = true;
return;
}
@@ -303,7 +317,8 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
m_HistoryCollector.AddUnaryOpToHistory((int)wParam, m_bInv, m_angletype);
}
if ((wParam == IDC_SIN) || (wParam == IDC_COS) || (wParam == IDC_TAN) || (wParam == IDC_SINH) || (wParam == IDC_COSH) || (wParam == IDC_TANH))
if ((wParam == IDC_SIN) || (wParam == IDC_COS) || (wParam == IDC_TAN) || (wParam == IDC_SINH) || (wParam == IDC_COSH) || (wParam == IDC_TANH)
|| (wParam == IDC_SEC) || (wParam == IDC_CSC) || (wParam == IDC_COT) || (wParam == IDC_SECH) || (wParam == IDC_CSCH) || (wParam == IDC_COTH))
{
if (IsCurrentTooBigForTrig())
{
@@ -330,9 +345,13 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
/* reset the m_bInv flag and indicators if it is set
and have been used */
if (m_bInv
&& ((wParam == IDC_CHOP) || (wParam == IDC_SIN) || (wParam == IDC_COS) || (wParam == IDC_TAN) || (wParam == IDC_LN) || (wParam == IDC_DMS)
|| (wParam == IDC_DEGREES) || (wParam == IDC_SINH) || (wParam == IDC_COSH) || (wParam == IDC_TANH)))
if (m_bInv &&
((wParam == IDC_CHOP) || (wParam == IDC_SIN) || (wParam == IDC_COS) ||
(wParam == IDC_TAN) || (wParam == IDC_LN) || (wParam == IDC_DMS) ||
(wParam == IDC_DEGREES) || (wParam == IDC_SINH) || (wParam == IDC_COSH) ||
(wParam == IDC_TANH) || (wParam == IDC_SEC) || (wParam == IDC_CSC) ||
(wParam == IDC_COT) || (wParam == IDC_SECH) || (wParam == IDC_CSCH) ||
(wParam == IDC_COTH)))
{
m_bInv = false;
}
@@ -341,10 +360,10 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
}
// Tiny binary edit windows clicked. Toggle that bit and update display
if (IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITSTART + 63))
if (IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITEND))
{
// Same reasoning as for unary operators. We need to seed it previous number
if (m_nLastCom >= IDC_AND && m_nLastCom <= IDC_PWR)
if (IsBinOpCode(m_nLastCom))
{
m_currentVal = m_lastVal;
}
@@ -377,6 +396,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
m_precedenceOpCount = m_nTempCom = m_nLastCom = m_nOpCode = 0;
m_nPrevOpCode = 0;
m_bNoPrevEqu = true;
m_carryBit = 0;
/* clear the parenthesis status box indicator, this will not be
cleared for CENTR */
@@ -700,7 +720,6 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
case IDC_MCLEAR:
m_memoryValue = make_unique<Rational>(wParam == IDC_STORE ? TruncateNumForIntMath(m_currentVal) : 0);
break;
case IDC_PI:
if (!m_fIntegerMode)
{
@@ -713,7 +732,43 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
}
HandleErrorCommand(wParam);
break;
case IDC_RAND:
if (!m_fIntegerMode)
{
CheckAndAddLastBinOpToHistory(); // rand is like entering the number
wstringstream str;
str << fixed << setprecision(m_precision) << GenerateRandomNumber();
auto rat = StringToRat(false, str.str(), false, L"", m_radix, m_precision);
if (rat != nullptr)
{
m_currentVal = Rational{ rat };
}
else
{
m_currentVal = Rational{ 0 };
}
destroyrat(rat);
DisplayNum();
m_bInv = false;
break;
}
HandleErrorCommand(wParam);
break;
case IDC_EULER:
if (!m_fIntegerMode)
{
CheckAndAddLastBinOpToHistory(); // e is like entering the number
m_currentVal = Rational{ rat_exp };
DisplayNum();
m_bInv = false;
break;
}
HandleErrorCommand(wParam);
break;
case IDC_FE:
// Toggle exponential notation display.
m_nFE = NUMOBJ_FMT(!(int)m_nFE);
@@ -761,7 +816,7 @@ void CCalcEngine::ResolveHighestPrecedenceOperation()
{
m_currentVal = m_holdVal;
DisplayNum(); // to update the m_numberString
m_HistoryCollector.AddBinOpToHistory(m_nOpCode, false);
m_HistoryCollector.AddBinOpToHistory(m_nOpCode, m_fIntegerMode, false);
m_HistoryCollector.AddOpndToHistory(m_numberString, m_currentVal); // Adding the repeated last op to history
}
@@ -863,11 +918,14 @@ struct FunctionNameElement
wstring gradString;
wstring inverseGradString; // Will fall back to gradString if empty
wstring programmerModeString;
bool hasAngleStrings = ((!radString.empty()) || (!inverseRadString.empty()) || (!gradString.empty()) || (!inverseGradString.empty()));
};
// Table for each unary operator
static const std::unordered_map<int, FunctionNameElement> unaryOperatorStringTable = {
static const std::unordered_map<int, FunctionNameElement> operatorStringTable =
{
{ IDC_CHOP, { L"", SIDS_FRAC } },
{ IDC_SIN, { SIDS_SIND, SIDS_ASIND, SIDS_SINR, SIDS_ASINR, SIDS_SING, SIDS_ASING } },
@@ -878,6 +936,14 @@ static const std::unordered_map<int, FunctionNameElement> unaryOperatorStringTab
{ IDC_COSH, { L"", SIDS_ACOSH } },
{ IDC_TANH, { L"", SIDS_ATANH } },
{ IDC_SEC, { SIDS_SECD, SIDS_ASECD, SIDS_SECR, SIDS_ASECR, SIDS_SECG, SIDS_ASECG } },
{ IDC_CSC, { SIDS_CSCD, SIDS_ACSCD, SIDS_CSCR, SIDS_ACSCR, SIDS_CSCG, SIDS_ACSCG } },
{ IDC_COT, { SIDS_COTD, SIDS_ACOTD, SIDS_COTR, SIDS_ACOTR, SIDS_COTG, SIDS_ACOTG } },
{ IDC_SECH, { SIDS_SECH, SIDS_ASECH } },
{ IDC_CSCH, { SIDS_CSCH, SIDS_ACSCH } },
{ IDC_COTH, { SIDS_COTH, SIDS_ACOTH } },
{ IDC_LN, { L"", SIDS_POWE } },
{ IDC_SQR, { SIDS_SQR } },
{ IDC_CUB, { SIDS_CUBE } },
@@ -885,7 +951,19 @@ static const std::unordered_map<int, FunctionNameElement> unaryOperatorStringTab
{ IDC_REC, { SIDS_RECIPROC } },
{ IDC_DMS, { L"", SIDS_DEGREES } },
{ IDC_SIGN, { SIDS_NEGATE } },
{ IDC_DEGREES, { SIDS_DEGREES } }
{ IDC_DEGREES, { SIDS_DEGREES } },
{ IDC_POW2, { SIDS_TWOPOWX } },
{ IDC_LOGBASEX, { SIDS_LOGBASEX } },
{ IDC_ABS, { SIDS_ABS } },
{ IDC_CEIL, { SIDS_CEIL } },
{ IDC_FLOOR, { SIDS_FLOOR } },
{ IDC_NAND, { SIDS_NAND } },
{ IDC_NOR, { SIDS_NOR } },
{ IDC_RSHFL, { SIDS_RSH } },
{ IDC_RORC, { SIDS_ROR } },
{ IDC_ROLC, { SIDS_ROL } },
{ IDC_CUBEROOT, {SIDS_CUBEROOT} },
{ IDC_MOD, {SIDS_MOD, L"", L"", L"", L"", L"", SIDS_PROGRAMMER_MOD} },
};
wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype)
@@ -893,7 +971,7 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE
// Try to lookup the ID in the UFNE table
wstring ids = L"";
if (auto pair = unaryOperatorStringTable.find(nOpCode); pair != unaryOperatorStringTable.end())
if (auto pair = operatorStringTable.find(nOpCode); pair != operatorStringTable.end())
{
const FunctionNameElement& element = pair->second;
if (!element.hasAngleStrings || ANGLE_DEG == angletype)
@@ -941,6 +1019,32 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE
return OpCodeToString(nOpCode);
}
wstring_view CCalcEngine::OpCodeToBinaryString(int nOpCode, bool isIntegerMode)
{
// Try to lookup the ID in the UFNE table
wstring ids = L"";
if (auto pair = operatorStringTable.find(nOpCode); pair != operatorStringTable.end())
{
if (isIntegerMode && !pair->second.programmerModeString.empty())
{
ids = pair->second.programmerModeString;
}
else
{
ids = pair->second.degreeString;
}
}
if (!ids.empty())
{
return GetString(ids);
}
// If we didn't find an ID in the table, use the op code.
return OpCodeToString(nOpCode);
}
bool CCalcEngine::IsCurrentTooBigForTrig()
{
return m_currentVal >= m_maxTrigonometricNum;
@@ -1007,3 +1111,14 @@ wstring CCalcEngine::GetStringForDisplay(Rational const& rat, uint32_t radix)
return result;
}
double CCalcEngine::GenerateRandomNumber()
{
if (m_randomGeneratorEngine == nullptr)
{
random_device rd;
m_randomGeneratorEngine = std::make_unique<std::mt19937>(rd());
m_distr = std::make_unique<std::uniform_real_distribution<>>(0, 1);
}
return (*m_distr.get())(*m_randomGeneratorEngine.get());
}

View File

@@ -46,8 +46,8 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r
}
break;
// Rotate Left with hi bit wrapped over to lo bit
case IDC_ROL:
case IDC_ROLC:
if (m_fIntegerMode)
{
result = Integer(rat);
@@ -55,14 +55,23 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r
uint64_t w64Bits = result.ToUInt64_t();
uint64_t msb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1;
w64Bits <<= 1; // LShift by 1
w64Bits |= msb; // Set the prev Msb as the current Lsb
if (op == IDC_ROL)
{
w64Bits |= msb; // Set the prev Msb as the current Lsb
}
else
{
w64Bits |= m_carryBit; // Set the carry bit as the LSB
m_carryBit = msb; // Store the msb as the next carry bit
}
result = w64Bits;
}
break;
// Rotate right with lo bit wrapped over to hi bit
case IDC_ROR:
case IDC_RORC:
if (m_fIntegerMode)
{
result = Integer(rat);
@@ -70,7 +79,16 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r
uint64_t w64Bits = result.ToUInt64_t();
uint64_t lsb = ((w64Bits & 0x01) == 1) ? 1 : 0;
w64Bits >>= 1; // RShift by 1
w64Bits |= (lsb << (m_dwWordBitWidth - 1));
if (op == IDC_ROR)
{
w64Bits |= (lsb << (m_dwWordBitWidth - 1));
}
else
{
w64Bits |= (m_carryBit << (m_dwWordBitWidth - 1));
m_carryBit = lsb;
}
result = w64Bits;
}
@@ -133,6 +151,48 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r
}
break;
case IDC_SEC:
if (!m_fIntegerMode)
{
result = m_bInv ? ACos(Invert(rat), m_angletype) : Invert(Cos(rat, m_angletype));
}
break;
case IDC_CSC:
if (!m_fIntegerMode)
{
result = m_bInv ? ASin(Invert(rat), m_angletype) : Invert(Sin(rat, m_angletype));
}
break;
case IDC_COT:
if (!m_fIntegerMode)
{
result = m_bInv ? ATan(Invert(rat), m_angletype) : Invert(Tan(rat, m_angletype));
}
break;
case IDC_SECH:
if (!m_fIntegerMode)
{
result = m_bInv ? ACosh(Invert(rat)) : Invert(Cosh(rat));
}
break;
case IDC_CSCH:
if (!m_fIntegerMode)
{
result = m_bInv ? ASinh(Invert(rat)) : Invert(Sinh(rat));
}
break;
case IDC_COTH:
if (!m_fIntegerMode)
{
result = m_bInv ? ATanh(Invert(rat)) : Invert(Tanh(rat));
}
break;
case IDC_REC: /* Reciprocal. */
result = Invert(rat);
break;
@@ -158,6 +218,10 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r
result = Pow(10, rat);
break;
case IDC_POW2:
result = Pow(2, rat);
break;
case IDC_LN: /* Functions for natural log. */
result = m_bInv ? Exp(rat) : Log(rat);
break;
@@ -202,6 +266,18 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r
}
break;
}
case IDC_CEIL:
result = (Frac(rat) > 0) ? Integer(rat + 1) : Integer(rat);
break;
case IDC_FLOOR:
result = (Frac(rat) < 0) ? Integer(rat - 1 ) : Integer(rat);
break;
case IDC_ABS:
result = Abs(rat);
break;
} // end switch( op )
}
catch (uint32_t nErrCode)

View File

@@ -28,6 +28,14 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
result ^= rhs;
break;
case IDC_NAND:
result = (result & rhs) ^ m_chopNumbers[m_numwidth];
break;
case IDC_NOR:
result = (result | rhs) ^ m_chopNumbers[m_numwidth];
break;
case IDC_RSHF:
{
if (m_fIntegerMode && result >= m_dwWordBitWidth) // Lsh/Rsh >= than current word size is always 0
@@ -52,7 +60,16 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
}
break;
}
case IDC_RSHFL:
{
if (m_fIntegerMode && result >= m_dwWordBitWidth) // Lsh/Rsh >= than current word size is always 0
{
throw CALC_E_NORESULT;
}
result = rhs >> result;
break;
}
case IDC_LSHF:
if (m_fIntegerMode && result >= m_dwWordBitWidth) // Lsh/Rsh >= than current word size is always 0
{
@@ -140,6 +157,10 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
case IDC_ROOT: // Calculates rhs to the result(th) root.
result = Root(rhs, result);
break;
case IDC_LOGBASEX:
result = (Log(result) / Log(rhs));
break;
}
}
catch (uint32_t dwErrCode)