Hello GitHub

This commit is contained in:
Howard Wolosky
2019-01-28 16:24:37 -08:00
parent 456fe5e355
commit c13b8a099e
822 changed files with 276650 additions and 75 deletions

View File

@@ -0,0 +1,317 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Header Files/CalcEngine.h"
using namespace std;
using namespace CalcEngine;
constexpr int C_NUM_MAX_DIGITS = MAX_STRLEN;
constexpr int C_EXP_MAX_DIGITS = 4;
void CalcNumSec::Clear()
{
value.clear();
m_isNegative = false;
}
void CalcInput::Clear()
{
m_base.Clear();
m_exponent.Clear();
m_hasExponent = false;
m_hasDecimal = false;
m_decPtIndex = 0;
}
bool CalcInput::TryToggleSign(bool isIntegerMode, wstring_view maxNumStr)
{
// Zero is always positive
if (m_base.IsEmpty())
{
m_base.IsNegative(false);
m_exponent.IsNegative(false);
}
else if (m_hasExponent)
{
m_exponent.IsNegative(!m_exponent.IsNegative());
}
else
{
// When in integer only mode, it isnt always allowed to toggle, as toggling can cause the num to be out of
// bounds. For eg. in byte -128 is valid, but when it toggled it becomes 128, which is more than 127.
if (isIntegerMode && m_base.IsNegative())
{
// Decide if this additional digit will fit for the given bit width
if (m_base.value.size() >= maxNumStr.size() && m_base.value.back() > maxNumStr.back())
{
// Last digit is more than the allowed positive number. Fail
return false;
}
}
m_base.IsNegative(!m_base.IsNegative());
}
return true;
}
bool CalcInput::TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMode, wstring_view maxNumStr, long wordBitWidth, int maxDigits)
{
// Convert from an integer into a character
// This includes both normal digits and alpha 'digits' for radices > 10
auto chDigit = static_cast<wchar_t>((value < 10) ? (L'0' + value) : (L'A' + value - 10));
CalcNumSec* pNumSec;
size_t maxCount;
if (m_hasExponent)
{
pNumSec = &m_exponent;
maxCount = C_EXP_MAX_DIGITS;
}
else
{
pNumSec = &m_base;
maxCount = maxDigits;
// Don't include the decimal point in the count. In that way you can enter the maximum allowed precision.
// Precision doesnt include decimal point.
if (HasDecimalPt())
{
maxCount++;
}
// First leading 0 is not counted in input restriction as the output can be of that form
// See NumberToString algorithm. REVIEW: We dont have such input restriction mimicking based on output of NumberToString for exponent
// NumberToString can give 10 digit exponent, but we still restrict the exponent here to be only 4 digits.
if (!pNumSec->IsEmpty() && pNumSec->value.front() == L'0')
{
maxCount++;
}
}
// Ignore leading zeros
if (pNumSec->IsEmpty() && (value == 0))
{
return true;
}
if (pNumSec->value.size() < maxCount)
{
pNumSec->value += chDigit;
return true;
}
// if we are in integer mode, within the base, and we're on the last digit then
// there are special cases where we can actually add one more digit.
if (isIntegerMode && pNumSec->value.size() == maxCount && !m_hasExponent)
{
bool allowExtraDigit = false;
if (radix == 8)
{
switch (wordBitWidth % 3)
{
case 1:
// in 16 or 64bit word size, if the first digit is a 1 we can enter 6 (16bit) or 22 (64bit) digits
allowExtraDigit = (pNumSec->value.front() == L'1');
break;
case 2:
// in 8 or 32bit word size, if the first digit is a 3 or less we can enter 3 (8bit) or 11 (32bit) digits
allowExtraDigit = (pNumSec->value.front() <= L'3');
break;
}
}
else if (radix == 10)
{
// If value length is at least the max, we know we can't add another digit.
if(pNumSec->value.size() < maxNumStr.size())
{
// Compare value to substring of maxNumStr of value.size() length.
// If cmpResult > 0:
// eg. max is "127", and the current number is "20". first digit itself says we are out.
// Additional digit is not possible
// If cmpResult < 0:
// Success case. eg. max is "127", and current number is say "11". The second digit '1' being <
// corresponding digit '2', means all digits are possible to append, like 119 will still be < 127
// If cmpResult == 0:
// Undecided still. The case when max is "127", and current number is "12". Look for the new number being 7 or less to allow
auto cmpResult = pNumSec->value.compare(0, wstring::npos, maxNumStr, 0, pNumSec->value.size());
if (cmpResult < 0)
{
allowExtraDigit = true;
}
else if (cmpResult == 0)
{
auto lastChar = maxNumStr[pNumSec->value.size()];
if (chDigit <= lastChar)
{
allowExtraDigit = true;
}
else if (pNumSec->IsNegative() && chDigit <= lastChar + 1)
{
// Negative value case, eg. max is "127", and current number is "-12". Then 8 is also valid, as the range
// is always from -(max+1)...max in signed mode
allowExtraDigit = true;
}
}
}
}
if (allowExtraDigit)
{
pNumSec->value += chDigit;
return true;
}
}
return false;
}
bool CalcInput::TryAddDecimalPt()
{
// Already have a decimal pt or we're in the exponent
if (m_hasDecimal || m_hasExponent)
{
return false;
}
if (m_base.IsEmpty())
{
m_base.value += L"0"; // Add a leading zero
}
m_decPtIndex = m_base.value.size();
m_base.value += m_decSymbol;
m_hasDecimal = true;
return true;
}
bool CalcInput::HasDecimalPt()
{
return m_hasDecimal;
}
bool CalcInput::TryBeginExponent()
{
// For compatibility, add a trailing dec point to base num if it doesn't have one
TryAddDecimalPt();
if (m_hasExponent) // Already entering exponent
{
return false;
}
m_hasExponent = true; // Entering exponent
return true;
}
void CalcInput::Backspace()
{
if (m_hasExponent)
{
if (!m_exponent.IsEmpty())
{
m_exponent.value.pop_back();
if (m_exponent.IsEmpty())
{
m_exponent.Clear();
}
}
else
{
m_hasExponent = false;
}
}
else
{
if (!m_base.IsEmpty())
{
m_base.value.pop_back();
}
if (m_base.value.size() <= m_decPtIndex)
{
// Backed up over decimal point
m_hasDecimal = false;
m_decPtIndex = 0;
}
if (m_base.IsEmpty())
{
m_base.Clear();
}
}
}
void CalcInput::SetDecimalSymbol(wchar_t decSymbol)
{
if (m_decSymbol != decSymbol)
{
m_decSymbol = decSymbol;
if (m_hasDecimal)
{
// Change to new decimal pt
m_base.value[m_decPtIndex] = m_decSymbol;
}
}
}
wstring CalcInput::ToString(uint32_t radix, bool isIntegerMode)
{
// In theory both the base and exponent could be C_NUM_MAX_DIGITS long.
wstringstream resStream;
if ((m_base.value.size() > MAX_STRLEN) || (m_hasExponent && m_exponent.value.size() > MAX_STRLEN))
{
return wstring();
}
if (m_base.IsNegative())
{
resStream << L'-';
}
resStream << (m_base.IsEmpty() ? L"0" : m_base.value);
if (m_hasExponent)
{
// Add a decimal point if it is not already there
if (!m_hasDecimal)
{
resStream << m_decSymbol;
}
resStream << ((radix == 10) ? L'e' : L'^');
resStream << (m_exponent.IsNegative() ? L'-' : L'+');
resStream << (m_exponent.IsEmpty() ? L"0" : m_exponent.value);
}
auto result = resStream.str();
// Base and Exp can each be up to C_NUM_MAX_DIGITS in length, plus 4 characters for sign, dec, exp, and expSign.
if (result.size() > C_NUM_MAX_DIGITS * 2 + 4)
{
return wstring();
}
return result;
}
Rational CalcInput::ToRational(uint32_t radix, int32_t precision)
{
PRAT rat = StringToRat(m_base.IsNegative(), m_base.value, m_exponent.IsNegative(), m_exponent.value, radix, precision);
if (rat == nullptr)
{
return Rational{};
}
Rational result{ rat };
destroyrat(rat);
return result;
}

View File

@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Header Files/CalcEngine.h"
bool IsOpInRange(WPARAM op, uint32_t x, uint32_t y)
{
return ((op >= x) && (op <= y));
}
bool IsBinOpCode(WPARAM opCode)
{
return IsOpInRange(opCode, IDC_AND, IDC_PWR);
}
// WARNING: IDC_SIGN is a special unary op but still this doesnt catch this. Caller has to be aware
// of it and catch it themself or not needing this
bool IsUnaryOpCode(WPARAM opCode)
{
return IsOpInRange(opCode, IDC_UNARYFIRST, IDC_UNARYLAST);
}
bool IsDigitOpCode(WPARAM opCode)
{
return IsOpInRange(opCode, IDC_0, IDC_F);
}
// Some commands are not affecting the state machine state of the calc flow. But these are more of
// some gui mode kind of settings (eg Inv button, or Deg,Rad , Back etc.). This list is getting bigger & bigger
// so we abstract this as a separate routine. Note: There is another side to this. Some commands are not
// gui mode setting to begin with, but once it is discovered it is invalid and we want to behave as though it
// was never inout, we need to revert the state changes made as a result of this test
bool IsGuiSettingOpCode(WPARAM opCode)
{
if (IsOpInRange(opCode, IDM_HEX, IDM_BIN) ||
IsOpInRange(opCode, IDM_QWORD, IDM_BYTE) ||
IsOpInRange(opCode, IDM_DEG, IDM_GRAD))
{
return true;
}
switch (opCode)
{
case IDC_INV:
case IDC_FE:
case IDC_MCLEAR:
case IDC_BACK:
case IDC_EXP:
case IDC_STORE:
case IDC_MPLUS:
case IDC_MMINUS:
return true;
}
// most of the commands
return false;
}

View File

@@ -0,0 +1,490 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#pragma once
#include "Header Files/CalcEngine.h"
#include "Command.h"
#include "CalculatorVector.h"
#include "ExpressionCommand.h"
#include "CalcException.h"
constexpr int ASCII_0 = 48;
using namespace std;
void CHistoryCollector::ReinitHistory()
{
m_lastOpStartIndex = -1;
m_lastBinOpStartIndex = -1;
m_curOperandIndex = 0;
m_bLastOpndBrace = false;
if (m_spTokens != nullptr)
{
m_spTokens->Clear();
}
if (m_spCommands != nullptr)
{
m_spCommands->Clear();
}
}
// Constructor
// Can throw Out of memory error
CHistoryCollector::CHistoryCollector(ICalcDisplay *pCalcDisplay, std::shared_ptr<IHistoryDisplay> pHistoryDisplay, wchar_t decimalSymbol) :
m_pHistoryDisplay(pHistoryDisplay),
m_pCalcDisplay(pCalcDisplay),
m_decimalSymbol(decimalSymbol),
m_iCurLineHistStart(-1)
{
ReinitHistory();
}
CHistoryCollector::~CHistoryCollector()
{
m_pHistoryDisplay = nullptr;
m_pCalcDisplay = nullptr;
if (m_spTokens != nullptr)
{
m_spTokens->Clear();
}
}
void CHistoryCollector::AddOpndToHistory(wstring_view numStr, PRAT hNoNum, bool fRepetition)
{
std::shared_ptr<CalculatorVector<int>> commands = std::make_shared<CalculatorVector<int>>();
// Check for negate
bool fNegative = (numStr[0] == L'-');
bool fSciFmt = false;
bool fDecimal = false;
for (size_t i = (fNegative ? 1 : 0); i < numStr.length(); i++)
{
if (numStr[i] == m_decimalSymbol)
{
IFT(commands->Append(IDC_PNT));
if (!fSciFmt)
{
fDecimal = true;
}
}
else if (numStr[i] == L'e')
{
IFT(commands->Append(IDC_EXP));
fSciFmt = true;
}
else if (numStr[i] == L'-')
{
IFT(commands->Append(IDC_SIGN));
}
else if (numStr[i] == L'+')
{
// Ignore.
}
// Number
else
{
int num = static_cast<int>(numStr[i]) - ASCII_0;
num += IDC_0;
IFT(commands->Append(num));
}
}
auto operandCommand = std::make_shared<COpndCommand>(commands, fNegative, fDecimal, fSciFmt);
operandCommand->Initialize(hNoNum);
int iCommandEnd = AddCommand(operandCommand);
m_lastOpStartIndex = IchAddSzToEquationSz(numStr, iCommandEnd);
if (fRepetition)
{
SetExpressionDisplay();
}
m_bLastOpndBrace = false;
m_lastBinOpStartIndex = -1;
}
void CHistoryCollector::RemoveLastOpndFromHistory()
{
TruncateEquationSzFromIch(m_lastOpStartIndex);
SetExpressionDisplay();
m_lastOpStartIndex = -1;
// This will not restore the m_lastBinOpStartIndex, as it isnt possible to remove that also later
}
void CHistoryCollector::AddBinOpToHistory(int nOpCode, bool fNoRepetition)
{
int iCommandEnd = AddCommand(std::make_shared<CBinaryCommand>(nOpCode));
m_lastBinOpStartIndex = IchAddSzToEquationSz(L" ", -1);
IchAddSzToEquationSz(CCalcEngine::OpCodeToString(nOpCode), iCommandEnd);
IchAddSzToEquationSz(L" ", -1);
if (fNoRepetition)
{
SetExpressionDisplay();
}
m_lastOpStartIndex = -1;
}
// 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 happenned. 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)
{
TruncateEquationSzFromIch(m_lastBinOpStartIndex);
if (fPrecInvToHigher)
{
EnclosePrecInvertionBrackets();
}
AddBinOpToHistory(nOpCode);
}
void CHistoryCollector::PushLastOpndStart(int ichOpndStart)
{
int ich = (ichOpndStart == -1) ? m_lastOpStartIndex : ichOpndStart;
if (m_curOperandIndex < static_cast<int>(m_operandIndices.size()))
{
m_operandIndices[m_curOperandIndex++] = ich;
}
}
void CHistoryCollector::PopLastOpndStart()
{
if (m_curOperandIndex > 0)
{
m_lastOpStartIndex = m_operandIndices[--m_curOperandIndex];
}
}
void CHistoryCollector::AddOpenBraceToHistory()
{
int iCommandEnd = AddCommand(std::make_shared<CParentheses>(IDC_OPENP));
int ichOpndStart = IchAddSzToEquationSz(CCalcEngine::OpCodeToString(IDC_OPENP), -1);
PushLastOpndStart(ichOpndStart);
SetExpressionDisplay();
m_lastBinOpStartIndex = -1;
}
void CHistoryCollector::AddCloseBraceToHistory()
{
int iCommandEnd = AddCommand(std::make_shared<CParentheses>(IDC_CLOSEP));
IchAddSzToEquationSz(CCalcEngine::OpCodeToString(IDC_CLOSEP), -1);
SetExpressionDisplay();
PopLastOpndStart();
m_lastBinOpStartIndex = -1;
m_bLastOpndBrace = true;
}
void CHistoryCollector::EnclosePrecInvertionBrackets()
{
// Top of the Opnd starts index or 0 is nothing is in top
int ichStart = (m_curOperandIndex > 0) ? m_operandIndices[m_curOperandIndex - 1] : 0;
InsertSzInEquationSz(CCalcEngine::OpCodeToString(IDC_OPENP), -1, ichStart);
IchAddSzToEquationSz(CCalcEngine::OpCodeToString(IDC_CLOSEP), -1);
}
bool CHistoryCollector::FOpndAddedToHistory()
{
return (-1 != m_lastOpStartIndex);
}
// AddUnaryOpToHistory
//
// This is does the postfix to prefix transalation of the input and adds the text to the history. Eg. doing 2 + 4 (sqrt),
// this routine will ensure the last sqrt call unary operator, actually goes back in history and wraps 4 in sqrt(4)
//
void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE angletype)
{
int iCommandEnd;
// When successfully applying a unary op, there should be an opnd already
// A very special case of % which is a funny post op unary op.
if (IDC_PERCENT == nOpCode)
{
iCommandEnd = AddCommand(std::make_shared<CUnaryCommand>(nOpCode));
IchAddSzToEquationSz(CCalcEngine::OpCodeToString(nOpCode), iCommandEnd);
}
else // all the other unary ops
{
std::shared_ptr<IOperatorCommand> spExpressionCommand;
if (IDC_SIGN == nOpCode)
{
spExpressionCommand = std::make_shared<CUnaryCommand>(nOpCode);
}
else
{
CalculationManager::Command angleOpCode;
if (angletype == ANGLE_DEG)
{
angleOpCode = CalculationManager::Command::CommandDEG;
}
if (angletype == ANGLE_RAD)
{
angleOpCode = CalculationManager::Command::CommandRAD;
}
if (angletype == ANGLE_GRAD)
{
angleOpCode = CalculationManager::Command::CommandGRAD;
}
int command = nOpCode;
switch (nOpCode)
{
case IDC_SIN:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandASIN) : IDC_SIN;
spExpressionCommand = std::make_shared<CUnaryCommand>(static_cast<int>(angleOpCode), command);
break;
case IDC_COS:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandACOS) : IDC_COS;
spExpressionCommand = std::make_shared<CUnaryCommand>(static_cast<int>(angleOpCode), command);
break;
case IDC_TAN:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandATAN) : IDC_TAN;
spExpressionCommand = std::make_shared<CUnaryCommand>(static_cast<int>(angleOpCode), command);
break;
case IDC_SINH:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandASINH) : IDC_SINH;
spExpressionCommand = std::make_shared<CUnaryCommand>(command);
break;
case IDC_COSH:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandACOSH) : IDC_COSH;
spExpressionCommand = std::make_shared<CUnaryCommand>(command);
break;
case IDC_TANH:
command = fInv ? static_cast<int>(CalculationManager::Command::CommandATANH) : IDC_TANH;
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);
break;
default:
spExpressionCommand = std::make_shared<CUnaryCommand>(nOpCode);
}
}
iCommandEnd = AddCommand(spExpressionCommand);
wstring operandStr{ CCalcEngine::OpCodeToUnaryString(nOpCode, fInv, angletype) };
if (!m_bLastOpndBrace) // The opnd is already covered in braces. No need for additional braces around it
{
operandStr.append(CCalcEngine::OpCodeToString(IDC_OPENP));
}
InsertSzInEquationSz(operandStr, iCommandEnd, m_lastOpStartIndex);
if (!m_bLastOpndBrace)
{
IchAddSzToEquationSz(CCalcEngine::OpCodeToString(IDC_CLOSEP), -1);
}
}
SetExpressionDisplay();
m_bLastOpndBrace = false;
// m_lastOpStartIndex remains the same as last opnd is just replaced by unaryop(lastopnd)
m_lastBinOpStartIndex = -1;
}
// Called after = with the result of the equation
// Responsible for clearing the top line of current running history display, as well as adding yet another element to
// history of equations
void CHistoryCollector::CompleteHistoryLine(wstring_view numStr)
{
if (nullptr != m_pCalcDisplay)
{
m_pCalcDisplay->SetExpressionDisplay(std::make_shared<CalculatorVector<std::pair<std::wstring, int>>>(), std::make_shared<CalculatorVector<std::shared_ptr<IExpressionCommand>>>());
}
if (nullptr != m_pHistoryDisplay)
{
unsigned int addedItemIndex = m_pHistoryDisplay->AddToHistory(m_spTokens, m_spCommands, numStr);
m_pCalcDisplay->OnHistoryItemAdded(addedItemIndex);
}
m_spTokens = nullptr;
m_spCommands = nullptr;
m_iCurLineHistStart = -1; // It will get recomputed at the first Opnd
ReinitHistory();
}
void CHistoryCollector::ClearHistoryLine(wstring_view errStr)
{
if (errStr.empty()) // in case of error let the display stay as it is
{
if (nullptr != m_pCalcDisplay)
{
m_pCalcDisplay->SetExpressionDisplay(std::make_shared<CalculatorVector<std::pair<std::wstring, int>>>(), std::make_shared<CalculatorVector<std::shared_ptr<IExpressionCommand>>>());
}
m_iCurLineHistStart = -1; // It will get recomputed at the first Opnd
ReinitHistory();
}
}
// Adds the given string psz to the globally maintained current equation string at the end.
// Also returns the 0 based index in the string just added. Can throw out of memory error
int CHistoryCollector::IchAddSzToEquationSz(wstring_view str, int icommandIndex)
{
if (m_spTokens == nullptr)
{
m_spTokens = std::make_shared<CalculatorVector<std::pair<std::wstring, int>>>();
}
if (FAILED(m_spTokens->Append(std::make_pair(wstring(str), icommandIndex))))
{
throw(CALC_E_OUTOFMEMORY);
}
unsigned int nTokens;
m_spTokens->GetSize(&nTokens);
return nTokens - 1;
}
// Inserts a given string into the global m_pszEquation at the given index ich taking care of reallocations etc.
void CHistoryCollector::InsertSzInEquationSz(wstring_view str, int icommandIndex, int ich)
{
if (FAILED(m_spTokens->InsertAt(ich, std::make_pair(wstring(str), icommandIndex))))
{
throw(CALC_E_OUTOFMEMORY);
}
}
// Chops off the current equation string from the given index
void CHistoryCollector::TruncateEquationSzFromIch(int ich)
{
// Truncate commands
int minIdx = -1;
unsigned int nTokens = 0;
std::pair<std::wstring, int> currentPair;
m_spTokens->GetSize(&nTokens);
for (unsigned int i = ich; i < nTokens; i++)
{
IFT(m_spTokens->GetAt(i, &currentPair));
int curTokenId = currentPair.second;
if (curTokenId != -1)
{
if ((minIdx != -1) || (curTokenId < minIdx))
{
minIdx = curTokenId;
IFT(m_spCommands->Truncate(minIdx));
}
}
}
IFT(m_spTokens->Truncate(ich));
}
// Adds the m_pszEquation into the running history text
void CHistoryCollector::SetExpressionDisplay()
{
if (nullptr != m_pCalcDisplay)
{
m_pCalcDisplay->SetExpressionDisplay(m_spTokens, m_spCommands);
}
}
int CHistoryCollector::AddCommand(_In_ const std::shared_ptr<IExpressionCommand> & spCommand)
{
if (m_spCommands == nullptr)
{
m_spCommands = std::make_shared <CalculatorVector<std::shared_ptr<IExpressionCommand>>>();
}
if (FAILED(m_spCommands->Append(spCommand)))
{
throw(CALC_E_OUTOFMEMORY);
}
unsigned int nCommmands = 0;
m_spCommands->GetSize(&nCommmands);
return nCommmands - 1;
}
//To Update the operands in the Expression according to the current Radix
void CHistoryCollector::UpdateHistoryExpression(uint32_t radix, int32_t precision)
{
if (m_spTokens != nullptr)
{
unsigned int size;
IFT(m_spTokens->GetSize(&size));
for (unsigned int i = 0; i < size; ++i)
{
std::pair<std::wstring, int> token;
IFT(m_spTokens->GetAt(i, &token));
int commandPosition = token.second;
if (commandPosition != -1)
{
std::shared_ptr<IExpressionCommand> expCommand;
IFT(m_spCommands->GetAt(commandPosition, &expCommand));
if (expCommand != nullptr && CalculationManager::CommandType::OperandCommand == expCommand->GetCommandType())
{
std::shared_ptr<COpndCommand> opndCommand = std::static_pointer_cast<COpndCommand>(expCommand);
if (opndCommand != nullptr)
{
token.first = opndCommand->GetString(radix, precision, m_decimalSymbol);
IFT(m_spTokens->SetAt(i, token));
opndCommand->SetCommands(GetOperandCommandsFromString(token.first));
}
}
}
}
SetExpressionDisplay();
}
}
void CHistoryCollector::SetDecimalSymbol(wchar_t decimalSymbol)
{
m_decimalSymbol = decimalSymbol;
}
//Update the commands corresponding to the passed string Number
std::shared_ptr<CalculatorVector<int>> CHistoryCollector::GetOperandCommandsFromString(wstring_view numStr)
{
std::shared_ptr<CalculatorVector<int>> commands = std::make_shared<CalculatorVector<int>>();
// Check for negate
bool fNegative = (numStr[0] == L'-');
bool fSciFmt = false;
bool fDecimal = false;
for (size_t i = (fNegative ? 1 : 0); i < numStr.length(); i++)
{
if (numStr[i] == m_decimalSymbol)
{
IFT(commands->Append(IDC_PNT));
fDecimal = true;
}
else if (numStr[i] == L'e')
{
IFT(commands->Append(IDC_EXP));
fSciFmt = true;
}
else if (numStr[i] == L'-')
{
IFT(commands->Append(IDC_SIGN));
}
else if (numStr[i] == L'+')
{
// Ignore.
}
// Number
else
{
int num = static_cast<int>(numStr[i]) - ASCII_0;
num += IDC_0;
IFT(commands->Append(num));
}
}
// If the number is negative, append a sign command at the end.
if (fNegative)
{
IFT(commands->Append(IDC_SIGN));
}
return commands;
}

View File

@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
#include "pch.h"
#include "Header Files/Number.h"
using namespace std;
namespace CalcEngine
{
Number::Number() noexcept :
Number(1, 0, { 0 })
{}
Number::Number(int32_t sign, int32_t exp, vector<uint32_t> const& mantissa) noexcept :
m_sign{ sign },
m_exp{ exp },
m_mantissa{ mantissa }
{}
Number::Number(PNUMBER p) noexcept :
m_sign{ p->sign },
m_exp{ p->exp },
m_mantissa{}
{
m_mantissa.reserve(p->cdigit);
copy(p->mant, p->mant + p->cdigit, back_inserter(m_mantissa));
}
PNUMBER Number::ToPNUMBER() const
{
PNUMBER ret = _createnum(static_cast<ULONG>(this->Mantissa().size()) + 1);
ret->sign = this->Sign();
ret->exp = this->Exp();
ret->cdigit = static_cast<long>(this->Mantissa().size());
MANTTYPE *ptrRet = ret->mant;
for (auto const& digit : this->Mantissa())
{
*ptrRet++ = digit;
}
return ret;
}
int32_t const& Number::Sign() const
{
return m_sign;
}
int32_t const& Number::Exp() const
{
return m_exp;
}
vector<uint32_t> const& Number::Mantissa() const
{
return m_mantissa;
}
bool Number::IsZero() const
{
return all_of(m_mantissa.begin(), m_mantissa.end(), [](auto &&i) { return i == 0; });
}
}

View File

@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
#include "pch.h"
#include "Header Files/Rational.h"
using namespace std;
namespace CalcEngine
{
Rational::Rational() noexcept :
m_p{},
m_q{ 1, 0, { 1 } }
{}
Rational::Rational(Number const& n) noexcept
{
int32_t qExp = 0;
if (n.Exp() < 0)
{
qExp -= n.Exp();
}
m_p = Number(n.Sign(), 0, n.Mantissa());
m_q = Number(1, qExp, { 1 });
}
Rational::Rational(Number const& p, Number const& q) noexcept :
m_p{ p },
m_q{ q }
{}
Rational::Rational(PRAT prat) noexcept :
m_p{ Number{prat->pp} },
m_q{ Number{prat->pq} }
{}
PRAT Rational::ToPRAT() const
{
PRAT ret = _createrat();
ret->pp = this->P().ToPNUMBER();
ret->pq = this->Q().ToPNUMBER();
return ret;
}
Number const& Rational::P() const
{
return m_p;
}
Number const& Rational::Q() const
{
return m_q;
}
bool Rational::IsZero() const
{
return this->P().IsZero();
}
}

View File

@@ -0,0 +1,220 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**************************************************************************\
*** SCICALC Scientific Calculator for Windows 3.00.12
*** (c)1989 Microsoft Corporation. All Rights Reserved.
***
*** scimain.c
***
*** Definitions of all globals, WinMain procedure
***
*** Last modification
*** Fri 22-Nov-1996
***
*** 22-Nov-1996
*** Converted Calc from floating point to infinite precision.
*** The new math engine is in ..\ratpak
***
***
*** 05-Jan-1990
*** Calc did not have a floating point exception signal handler. This
*** would cause CALC to be forced to exit on a FP exception as that's
*** the default.
*** The signal handler is defined in SCIFUNC.C, in WinMain we hook the
*** the signal.
\**************************************************************************/
#include "pch.h"
#include "Header Files/CalcEngine.h"
#include "CalculatorResource.h"
using namespace std;
using namespace CalcEngine;
/**************************************************************************/
/*** Global variable declarations and initializations ***/
/**************************************************************************/
static constexpr int DEFAULT_MAX_DIGITS = 32;
static constexpr int DEFAULT_PRECISION = 32;
static constexpr long DEFAULT_RADIX = 10;
static constexpr wchar_t DEFAULT_DEC_SEPARATOR = L'.';
static constexpr wchar_t DEFAULT_GRP_SEPARATOR = L',';
static constexpr wstring_view DEFAULT_GRP_STR = L"3;0";
static constexpr wstring_view DEFAULT_NUMBER_STR = L"0";
// Read strings for keys, errors, trig types, etc.
// These will be copied from the resources to local memory. A larger
// than needed block is allocated first and then reallocated once we
// know how much is actually used.
array<wstring, CSTRINGSENGMAX> CCalcEngine::s_engineStrings;
void CCalcEngine::LoadEngineStrings(CalculationManager::IResourceProvider& resourceProvider)
{
for (size_t i = 0; i < s_engineStrings.size(); i++)
{
s_engineStrings[i] = resourceProvider.GetCEngineString(g_sids[i]);
}
}
//////////////////////////////////////////////////
//
// InitialOneTimeOnlyNumberSetup
//
//////////////////////////////////////////////////
void CCalcEngine::InitialOneTimeOnlySetup(CalculationManager::IResourceProvider& resourceProvider)
{
LoadEngineStrings(resourceProvider);
// we must now setup all the ratpak constants and our arrayed pointers
// to these constants.
ChangeBaseConstants(DEFAULT_RADIX, DEFAULT_MAX_DIGITS, DEFAULT_PRECISION);
}
//////////////////////////////////////////////////
//
// CCalcEngine::CCalcEngine
//
//////////////////////////////////////////////////
CCalcEngine::CCalcEngine(bool fPrecedence, bool fIntegerMode, CalculationManager::IResourceProvider* const pResourceProvider, __in_opt ICalcDisplay *pCalcDisplay, __in_opt shared_ptr<IHistoryDisplay> pHistoryDisplay) :
m_HistoryCollector(pCalcDisplay, pHistoryDisplay, DEFAULT_DEC_SEPARATOR),
m_resourceProvider(pResourceProvider),
m_bSetCalcState(false),
m_fPrecedence(fPrecedence),
m_fIntegerMode(fIntegerMode),
m_pCalcDisplay(pCalcDisplay),
m_input(DEFAULT_DEC_SEPARATOR),
m_nOpCode(0),
m_nPrevOpCode(0),
m_openParenCount(0),
m_nPrecNum(0),
m_nTempCom(0),
m_nLastCom(0),
m_parenVals{},
m_precedenceVals{},
m_bChangeOp(false),
m_bRecord(false),
m_bError(false),
m_bInv(false),
m_nFE(FMT_FLOAT),
m_bNoPrevEqu(true),
m_numwidth(QWORD_WIDTH),
m_angletype(ANGLE_DEG),
m_radix(DEFAULT_RADIX),
m_precision(DEFAULT_PRECISION),
m_cIntDigitsSav(DEFAULT_MAX_DIGITS),
m_decGrouping(),
m_groupSeparator(DEFAULT_GRP_SEPARATOR),
m_numberString(DEFAULT_NUMBER_STR),
m_nOp(),
m_nPrecOp(),
m_memoryValue{make_unique<Rational>()},
m_holdVal{},
m_currentVal{},
m_lastVal{}
{
InitChopNumbers();
m_dwWordBitWidth = DwWordBitWidthFromeNumWidth(m_numwidth);
PRAT maxTrig = longtorat(10L);
PRAT hundred = longtorat(100L);
powrat(&maxTrig, hundred, m_radix, m_precision);
m_maxTrigonometricNum = Rational{ maxTrig };
destroyrat(maxTrig);
destroyrat(hundred);
SetRadixTypeAndNumWidth(DEC_RADIX, m_numwidth);
SettingsChanged();
DisplayNum();
}
void CCalcEngine::InitChopNumbers()
{
// these rat numbers are set only once and then never change regardless of
// base or precision changes
assert(m_chopNumbers.size() >= 4);
m_chopNumbers[0] = Rational{ rat_qword };
m_chopNumbers[1] = Rational{ rat_dword };
m_chopNumbers[2] = Rational{ rat_word };
m_chopNumbers[3] = Rational{ rat_byte };
// initialize the max dec number you can support for each of the supported bit length
// this is basically max num in that width / 2 in integer
assert(m_chopNumbers.size() == m_maxDecimalValueStrings.size());
for (size_t i = 0; i < m_chopNumbers.size(); i++)
{
PRAT hno = m_chopNumbers[i].ToPRAT();
divrat(&hno, rat_two, m_precision);
intrat(&hno, m_radix, m_precision);
m_maxDecimalValueStrings[i] = NumObjToString(hno, 10, FMT_FLOAT, m_precision);
NumObjDestroy(&hno);
}
}
// Gets the number in memory for UI to keep it persisted and set it again to a different instance
// of CCalcEngine. Otherwise it will get destructed with the CalcEngine
unique_ptr<Rational> CCalcEngine::PersistedMemObject()
{
return move(m_memoryValue);
}
void CCalcEngine::PersistedMemObject(Rational const& memObject)
{
m_memoryValue = make_unique<Rational>(memObject);
}
void CCalcEngine::SettingsChanged()
{
wchar_t lastDec = m_decimalSeparator;
wstring decStr = m_resourceProvider->GetCEngineString(L"sDecimal");
m_decimalSeparator = decStr.empty() ? DEFAULT_DEC_SEPARATOR : decStr.at(0);
// Until it can be removed, continue to set ratpak decimal here
SetDecimalSeparator(m_decimalSeparator);
wchar_t lastSep = m_groupSeparator;
wstring sepStr = m_resourceProvider->GetCEngineString(L"sThousand");
m_groupSeparator = sepStr.empty() ? DEFAULT_GRP_SEPARATOR : sepStr.at(0);
auto lastDecGrouping = m_decGrouping;
wstring grpStr = m_resourceProvider->GetCEngineString(L"sGrouping");
m_decGrouping = DigitGroupingStringToGroupingVector(grpStr.empty() ? DEFAULT_GRP_STR : grpStr);
bool numChanged = false;
// if the grouping pattern or thousands symbol changed we need to refresh the display
if (m_decGrouping != lastDecGrouping || m_groupSeparator != lastSep)
{
numChanged = true;
}
// if the decimal symbol has changed we always do the following things
if (m_decimalSeparator != lastDec)
{
// Re-initialize member variables' decimal point.
m_input.SetDecimalSymbol(m_decimalSeparator);
m_HistoryCollector.SetDecimalSymbol(m_decimalSeparator);
// put the new decimal symbol into the table used to draw the decimal key
s_engineStrings[IDS_DECIMAL] = m_decimalSeparator;
// we need to redraw to update the decimal point button
numChanged = true;
}
if (numChanged)
{
DisplayNum();
}
}
wchar_t CCalcEngine::DecimalSeparator() const
{
return m_decimalSeparator;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,400 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/****************************Module*Header***********************************\
* Module Name: SCIDISP.C
*
* Module Descripton:
*
* Warnings:
*
* Created:
*
* Author:
\****************************************************************************/
#include "pch.h"
#include "Header Files/CalcEngine.h"
using namespace std;
using namespace CalcEngine;
constexpr int MAX_EXPONENT = 4;
constexpr uint32_t MAX_GROUPING_SIZE = 16;
constexpr wstring_view c_decPreSepStr = L"[+-]?(\\d*)[";
constexpr wstring_view c_decPostSepStr = L"]?(\\d*)(?:e[+-]?(\\d*))?$";
/****************************************************************************\
* void DisplayNum(void)
*
* Convert m_currentVal to a string in the current radix.
*
* Updates the following variables:
* m_currentVal, m_numberString
\****************************************************************************/
//
// State of calc last time DisplayNum was called
//
typedef struct {
PRAT hnoNum;
int32_t precision;
uint32_t radix;
INT nFE;
NUM_WIDTH numwidth;
bool fIntMath;
bool bRecord;
bool bUseSep;
} LASTDISP;
LASTDISP gldPrevious = { nullptr, -1, 0, -1, (NUM_WIDTH)-1, false, false, false };
// Truncates if too big, makes it a non negative - the number in rat. Doesn't do anything if not in INT mode
CalcEngine::Rational CCalcEngine::TruncateNumForIntMath(CalcEngine::Rational const& rat)
{
if (!m_fIntegerMode)
{
return rat;
}
PRAT tempRat = rat.ToPRAT();
// Truncate to an integer. Do not round here.
intrat(&tempRat, m_radix, m_precision);
PRAT chopRat = m_chopNumbers[m_numwidth].ToPRAT();
// Can be converting a dec -ve number to Hex/Oct/Bin rep. Use 2's completement form
// Check the range.
if (NumObjIsLess(tempRat, rat_zero, m_precision))
{
// if negative make positive by doing a twos complement
NumObjNegate(&tempRat);
subrat(&tempRat, rat_one, m_precision);
NumObjNot(&tempRat, true, chopRat, m_radix, m_precision);
}
andrat(&tempRat, chopRat, m_radix, m_precision);
destroyrat(chopRat);
Rational result{ tempRat };
destroyrat(tempRat);
return result;
}
void CCalcEngine::DisplayNum(void)
{
//
// Only change the display if
// we are in record mode -OR-
// this is the first time DisplayNum has been called, -OR-
// something important has changed since the last time DisplayNum was
// called.
//
PRAT curRat = m_currentVal.ToPRAT();
bool hasValChanged = !gldPrevious.hnoNum || !NumObjIsEq(gldPrevious.hnoNum, curRat, m_precision);
destroyrat(curRat);
if ( m_bRecord || gldPrevious.hnoNum == nullptr ||
hasValChanged ||
gldPrevious.precision != m_precision ||
gldPrevious.radix != m_radix ||
gldPrevious.nFE != (int)m_nFE ||
gldPrevious.bUseSep != true ||
gldPrevious.numwidth != m_numwidth ||
gldPrevious.fIntMath != m_fIntegerMode ||
gldPrevious.bRecord != m_bRecord )
{
gldPrevious.precision = m_precision;
gldPrevious.radix = m_radix;
gldPrevious.nFE = (int)m_nFE;
gldPrevious.numwidth = m_numwidth;
gldPrevious.fIntMath = m_fIntegerMode;
gldPrevious.bRecord = m_bRecord;
gldPrevious.bUseSep = true;
if (m_bRecord)
{
// Display the string and return.
m_numberString = m_input.ToString(m_radix, m_fIntegerMode);
}
else
{
// If we're in Programmer mode, perform integer truncation so e.g. 5 / 2 * 2 results in 4, not 5.
if (m_fIntegerMode)
{
m_currentVal = TruncateNumForIntMath(m_currentVal);
}
m_numberString = GetStringForDisplay(m_currentVal, m_radix);
}
// Displayed number can go thru transformation. So copy it after transformation
destroyrat(gldPrevious.hnoNum);
gldPrevious.hnoNum = m_currentVal.ToPRAT();
if((m_radix == 10) && IsNumberInvalid(m_numberString, MAX_EXPONENT, m_precision, m_radix))
{
DisplayError(CALC_E_OVERFLOW);
}
else
{
// Display the string and return.
SetPrimaryDisplay(GroupDigitsPerRadix(m_numberString, m_radix));
}
}
}
int CCalcEngine::IsNumberInvalid(const wstring& numberString, int iMaxExp, int iMaxMantissa, uint32_t radix) const
{
int iError = 0;
if (radix == 10)
{
// start with an optional + or -
// followed by zero or more digits
// followed by an optional decimal point
// followed by zero or more digits
// followed by an optional exponent
// in case there's an exponent:
// its optionally followed by a + or -
// which is followed by zero or more digits
wregex rx(wstring{ c_decPreSepStr } + m_decimalSeparator + wstring{ c_decPostSepStr });
wsmatch matches;
if (regex_match(numberString, matches, rx))
{
// Check that exponent isn't too long
if (matches.length(3) > iMaxExp)
{
iError = IDS_ERR_INPUT_OVERFLOW;
}
else
{
wstring exp = matches.str(1);
auto intItr = exp.begin();
auto intEnd = exp.end();
while (intItr != intEnd && *intItr == L'0')
{
intItr++;
}
auto iMantissa = distance(intItr, intEnd) + matches.length(2);
if (iMantissa > iMaxMantissa)
{
iError = IDS_ERR_INPUT_OVERFLOW;
}
}
}
else
{
iError = IDS_ERR_UNK_CH;
}
}
else
{
for (const wchar_t& c : numberString)
{
if (radix == 16)
{
if (!(iswdigit(c) || (c >= L'A' && c <= L'F')))
{
iError = IDS_ERR_UNK_CH;
}
}
else if (c < L'0' || c >= L'0' + radix)
{
iError = IDS_ERR_UNK_CH;
}
}
}
return iError;
}
/****************************************************************************\
*
* DigitGroupingStringToGroupingVector
*
* Description:
* This will take the digit grouping string found in the regional applet and
* represent this string as a vector.
*
* groupingString
* 0;0 - no grouping
* 3;0 - group every 3 digits
* 3 - group 1st 3, then no grouping after
* 3;0;0 - group 1st 3, then no grouping after
* 3;2;0 - group 1st 3 and then every 2 digits
* 4;0 - group every 4 digits
* 5;3;2;0 - group 5, then 3, then every 2
* 5;3;2 - group 5, then 3, then 2, then no grouping after
*
* Returns: the groupings as a vector
*
\****************************************************************************/
vector<uint32_t> CCalcEngine::DigitGroupingStringToGroupingVector(wstring_view groupingString)
{
vector<uint32_t> grouping{};
uint32_t currentGroup = 0;
wchar_t* next = nullptr;
for (const wchar_t* itr = groupingString.data(); *itr != L'\0'; ++itr)
{
// Try to parse a grouping number from the string
currentGroup = wcstoul(itr, &next, 10);
// If we successfully parsed a group, add it to the grouping.
if (currentGroup < MAX_GROUPING_SIZE)
{
grouping.emplace_back(currentGroup);
}
// If we found a grouping and aren't at the end of the string yet,
// jump to the next position in the string (the ';').
// The loop will then increment us to the next character, which should be a number.
if (next && (static_cast<size_t>(next - groupingString.data()) < groupingString.length()))
{
itr = next;
}
}
return grouping;
}
wstring CCalcEngine::GroupDigitsPerRadix(wstring_view numberString, uint32_t radix)
{
if (numberString.empty())
{
return wstring{};
}
switch (radix)
{
case 10:
return GroupDigits(wstring{ m_groupSeparator }, m_decGrouping, numberString, (L'-' == numberString[0]));
case 8:
return GroupDigits(L" ", { 3, 0 }, numberString);
case 2:
case 16:
return GroupDigits(L" ", { 4, 0 }, numberString);
default:
return wstring{ numberString };
}
}
/****************************************************************************\
*
* GroupDigits
*
* Description:
* This routine will take a grouping vector and the display string and
* add the separator according to the pattern indicated by the separator.
*
* Grouping
* 0,0 - no grouping
* 3,0 - group every 3 digits
* 3 - group 1st 3, then no grouping after
* 3,0,0 - group 1st 3, then no grouping after
* 3,2,0 - group 1st 3 and then every 2 digits
* 4,0 - group every 4 digits
* 5,3,2,0 - group 5, then 3, then every 2
* 5,3,2 - group 5, then 3, then 2, then no grouping after
*
\***************************************************************************/
wstring CCalcEngine::GroupDigits(wstring_view delimiter, vector<uint32_t> const& grouping, wstring_view displayString, bool isNumNegative)
{
// if there's nothing to do, bail
if (delimiter.empty() || grouping.empty())
{
return wstring{ displayString };
}
// Find the position of exponential 'e' in the string
size_t exp = displayString.find(L'e');
bool hasExponent = (exp != wstring_view::npos);
// Find the position of decimal point in the string
size_t dec = displayString.find(m_decimalSeparator);
bool hasDecimal = (dec != wstring_view::npos);
// Create an iterator that points to the end of the portion of the number subject to grouping (i.e. left of the decimal)
auto ritr = displayString.rend();
if (hasDecimal)
{
ritr -= dec;
}
else if (hasExponent)
{
ritr -= exp;
}
else
{
ritr = displayString.rbegin();
}
wstringstream groupedStream{};
uint32_t groupingSize = 0;
auto groupItr = grouping.begin();
auto currGrouping = *groupItr;
// Mark the 'end' of the string as either rend() or rend()-1 if there is a negative sign
// We exclude the sign here because we don't want to end up with e.g. "-,123,456"
// Then, iterate from back to front, adding group delimiters as needed.
auto reverse_end = displayString.rend() - (isNumNegative ? 1 : 0);
while (ritr != reverse_end)
{
groupedStream << *ritr++;
groupingSize++;
// If a group is complete, add a separator
// Do not add a separator if:
// - grouping size is 0
// - we are at the end of the digit string
if (currGrouping != 0 && (groupingSize % currGrouping) == 0 && ritr != reverse_end)
{
groupedStream << wstring{ delimiter };
groupingSize = 0; // reset for a new group
// Shift the grouping to next values if they exist
if (groupItr != grouping.end())
{
++groupItr;
// Loop through grouping vector until we find a non-zero value.
// "0" values may appear in a form of either e.g. "3;0" or "3;0;0".
// A 0 in the last position means repeat the previous grouping.
// A 0 in another position is a group. So, "3;0;0" means "group 3, then group 0 repeatedly"
// This could be expressed as just "3" but GetLocaleInfo is returning 3;0;0 in some cases instead.
for (currGrouping = 0; groupItr != grouping.end(); ++groupItr)
{
// If it's a non-zero value, that's our new group
if (*groupItr != 0)
{
currGrouping = *groupItr;
break;
}
// Otherwise, save the previous grouping in case we need to repeat it
currGrouping = *(groupItr - 1);
}
}
}
}
// now copy the negative sign if it is there
if (isNumNegative)
{
groupedStream << displayString[0];
}
auto groupedString = groupedStream.str();
wstring result(groupedString.rbegin(), groupedString.rend());
// Add the right (fractional or exponential) part of the number to the final string.
if (hasDecimal)
{
result += displayString.substr(dec);
}
else if (hasExponent)
{
result += displayString.substr(exp);
}
return result;
}

View File

@@ -0,0 +1,306 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**************************************************************************/
/*** SCICALC Scientific Calculator for Windows 3.00.12 ***/
/*** (c)1989 Microsoft Corporation. All Rights Reserved. ***/
/*** ***/
/*** scifunc.c ***/
/*** ***/
/*** Functions contained: ***/
/*** SciCalcFunctions--do sin, cos, tan, com, log, ln, rec, fac, etc.***/
/*** DisplayError--Error display driver. ***/
/*** ***/
/*** Functions called: ***/
/*** SciCalcFunctions call DisplayError. ***/
/*** ***/
/*** ***/
/**************************************************************************/
#include "pch.h"
#include "Header Files/CalcEngine.h"
using namespace std;
using namespace CalcEngine;
/* Routines for more complex mathematical functions/error checking. */
CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& rat, DWORD op)
{
PRAT tempRat = rat.ToPRAT();
try
{
switch (op)
{
case IDC_CHOP:
m_bInv ? fracrat(&tempRat , m_radix, m_precision) : intrat(&tempRat, m_radix, m_precision);
break;
/* Return complement. */
case IDC_COM:
{
PRAT chopRat = m_chopNumbers[m_numwidth].ToPRAT();
NumObjNot(&tempRat, m_fIntegerMode, chopRat, m_radix, m_precision);
destroyrat(chopRat);
break;
}
// Rotate Left with hi bit wrapped over to lo bit
case IDC_ROL:
if (m_fIntegerMode)
{
intrat(&tempRat, m_radix, m_precision);
PRAT curRat = m_currentVal.ToPRAT();
ULONGLONG w64Bits = NumObjGetUlValue(curRat, m_radix, m_precision);
ULONGLONG msb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1;
w64Bits <<= 1; // LShift by 1
w64Bits |= msb; // Set the prev Msb as the current Lsb
NumObjSetUlonglongValue(&curRat, w64Bits, m_radix, m_precision);
m_currentVal = Rational{ curRat };
destroyrat(curRat);
}
break;
// Rotate right with lo bit wrapped over to hi bit
case IDC_ROR:
if (m_fIntegerMode)
{
intrat(&tempRat, m_radix, m_precision);
PRAT curRat = m_currentVal.ToPRAT();
ULONGLONG w64Bits = NumObjGetUlValue(curRat, m_radix, m_precision);
ULONGLONG lsb = ((w64Bits & 0x01) == 1) ? 1 : 0;
w64Bits >>= 1; //RShift by 1
w64Bits |= (lsb << (m_dwWordBitWidth - 1));
NumObjSetUlonglongValue(&curRat, w64Bits, m_radix, m_precision);
m_currentVal = Rational{ curRat };
destroyrat(curRat);
}
break;
case IDC_PERCENT:
{
PRAT hno = nullptr;
PRAT hno100 = nullptr;
try
{
// If the operator is multiply/divide, we evaluate this as "X [op] (Y%)"
// Otherwise, we evaluate it as "X [op] (X * Y%)"
if (m_nOpCode == IDC_MUL || m_nOpCode == IDC_DIV)
{
NumObjSetIntValue(&hno100, 100);
divrat(&tempRat, hno100, m_precision);
destroyrat(hno100);
}
else
{
hno = m_lastVal.ToPRAT();
NumObjSetIntValue(&hno100, 100);
divrat(&hno, hno100, m_precision);
destroyrat(hno100);
mulrat(&tempRat, hno, m_precision);
destroyrat(hno);
}
}
catch ( DWORD nErrCode )
{
destroyrat(hno);
destroyrat(hno100);
destroyrat(tempRat);
throw nErrCode;
}
break;
}
case IDC_SIN: /* Sine; normal and arc */
if (!m_fIntegerMode)
{
m_bInv ? asinanglerat(&tempRat, m_angletype, m_radix, m_precision) : NumObjSin(&tempRat, m_angletype, m_radix, m_precision);
}
break;
case IDC_SINH: /* Sine- hyperbolic and archyperbolic */
if (!m_fIntegerMode)
{
m_bInv ? asinhrat(&tempRat, m_radix, m_precision) : sinhrat(&tempRat, m_radix, m_precision);
}
break;
case IDC_COS: /* Cosine, follows convention of sine function. */
if (!m_fIntegerMode)
{
m_bInv ? acosanglerat(&tempRat, m_angletype, m_radix, m_precision) : NumObjCos(&tempRat, m_angletype, m_radix, m_precision);
}
break;
case IDC_COSH: /* Cosine hyperbolic, follows convention of sine h function. */
if (!m_fIntegerMode)
{
m_bInv ? acoshrat(&tempRat, m_radix, m_precision) : coshrat(&tempRat, m_radix, m_precision);
}
break;
case IDC_TAN: /* Same as sine and cosine. */
if (!m_fIntegerMode)
{
m_bInv ? atananglerat(&tempRat, m_angletype, m_radix, m_precision) : NumObjTan(&tempRat, m_angletype, m_radix, m_precision);
}
break;
case IDC_TANH: /* Same as sine h and cosine h. */
if (!m_fIntegerMode)
{
m_bInv ? atanhrat(&tempRat, m_precision) : tanhrat(&tempRat, m_radix, m_precision);
}
break;
case IDC_REC: /* Reciprocal. */
NumObjInvert(&tempRat, m_precision);
break;
case IDC_SQR: /* Square */
powrat(&tempRat, rat_two, m_radix, m_precision);
break;
case IDC_SQRT: /* Sqrt only in Std mode */
rootrat(&tempRat, rat_two, m_radix, m_precision);
break;
case IDC_CUBEROOT:
case IDC_CUB: /* Cubing and cube root functions. */
{
PRAT hno = nullptr;
try
{
NumObjAssign(&hno, rat_one);
addrat(&hno, rat_two, m_precision);
if (IDC_CUBEROOT == op)
{
rootrat(&tempRat, hno, m_radix, m_precision);
}
else
{
powrat(&tempRat, hno, m_radix, m_precision);
}
destroyrat(hno);
}
catch (DWORD nErrCode)
{
destroyrat(hno);
destroyrat(tempRat);
throw nErrCode;
}
break;
}
case IDC_LOG: /* Functions for common log. */
log10rat(&tempRat, m_precision);
break;
case IDC_POW10:
NumObjAntiLog10(&tempRat, m_radix, m_precision);
break;
case IDC_LN: /* Functions for natural log. */
if (m_bInv)
{
exprat(&tempRat, m_radix, m_precision); // e^x.
}
else
{
lograt(&tempRat, m_precision);
}
break;
case IDC_FAC: /* Calculate factorial. Inverse is ineffective. */
factrat(&tempRat, m_radix, m_precision);
break;
case IDC_DEGREES:
ProcessCommand(IDC_INV);
// This case falls through to IDC_DMS case because in the old Win32 Calc,
// the degrees functionality was achieved as 'Inv' of 'dms' operation,
// so setting the IDC_INV command first and then performing 'dms' operation as global variables m_bInv, m_bRecord
// are set properly through ProcessCommand(IDC_INV)
case IDC_DMS:
{
if (!m_fIntegerMode)
{
PRAT hnoMin = nullptr;
PRAT hnoSec = nullptr;
PRAT hnoShft = nullptr;
try
{
NumObjSetIntValue(&hnoShft, m_bInv ? 100 : 60);
NumObjAssign(&hnoMin, tempRat);
intrat(&tempRat, m_radix, m_precision);
subrat(&hnoMin, tempRat, m_precision);
mulrat(&hnoMin, hnoShft, m_precision);
NumObjAssign(&hnoSec, hnoMin );
intrat(&hnoMin, m_radix, m_precision);
subrat(&hnoSec, hnoMin, m_precision);
mulrat(&hnoSec, hnoShft, m_precision);
//
// tempRat == degrees, hnoMin == minutes, hnoSec == seconds
//
NumObjSetIntValue(&hnoShft, m_bInv ? 60 : 100);
divrat(&hnoSec, hnoShft, m_precision);
addrat(&hnoMin, hnoSec, m_precision);
divrat(&hnoMin, hnoShft, m_precision);
addrat(&tempRat, hnoMin, m_precision);
destroyrat(hnoShft);
destroyrat(hnoMin);
destroyrat(hnoSec);
}
catch (DWORD nErrCode)
{
destroyrat(hnoShft);
destroyrat(hnoMin);
destroyrat(hnoSec);
destroyrat(tempRat);
throw nErrCode;
}
}
break;
}
} // end switch( op )
Rational result{ tempRat };
destroyrat(tempRat);
return result;
}
catch(DWORD nErrCode)
{
DisplayError(nErrCode);
destroyrat(tempRat);
return rat;
}
}
/* Routine to display error messages and set m_bError flag. Errors are */
/* called with DisplayError (n), where n is a DWORD between 0 and 5. */
void CCalcEngine::DisplayError(DWORD nError)
{
wstring errorString{ GetString(IDS_ERRORS_FIRST + SCODE_CODE(nError)) };
SetPrimaryDisplay(errorString, true /*isError*/);
m_bError = true; /* Set error flag. Only cleared with CLEAR or CENTR. */
m_HistoryCollector.ClearHistoryLine(errorString);
}

View File

@@ -0,0 +1,206 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Header Files/CalcEngine.h"
using namespace std;
/**************************************************************************\
* *
* *
* *
* # # ##### *
* # # # # # *
* # # # # # # # *
* # ### ### # # *
* # # ### # # # ### # # ### ##### # ### ### ### *
* # ## # # # ## # # # # # # ## # # # *
* # # # # # # # # # ##### # # ##### # *
* # # # # # # # # # # # # # # ## *
* # # # # # # # # # ### # # ### ### ## *
* *
* *
* Infinte Precision Production Version *
* *
\**************************************************************************/
//
// RETAIL version of NUMOBJ math that uses Infinite Precision
//
// History
//
// 16-Nov-1996 Wrote it
// whenever-97 Rewrote it using improved ratpak model
//
/*****************************************************************\
*
* Generic Math Package support routines and variables
*
* History:
* 01-Dec-1996 Wrote them
* whenever-97 Rewrote them
*
\*****************************************************************/
/*****************************************************************\
*
* Unary functions
*
* History:
* 01-Dec-1996 Wrote them
* whenever-97 Rewrote them
*
\*****************************************************************/
void NumObjInvert(PRAT * phno, int32_t precision)
{
PRAT hno = nullptr;
NumObjAssign( &hno, rat_one);
divrat( &hno, *phno, precision);
NumObjAssign( phno, hno );
NumObjDestroy( &hno );
}
void NumObjNegate(PRAT *phno)
{
(*phno)->pp->sign = -(*phno)->pp->sign;
}
void NumObjAbs(PRAT *phno)
{
(*phno)->pp->sign = 1;
(*phno)->pq->sign = 1;
}
void NumObjAntiLog10(PRAT *phno, uint32_t radix, int32_t precision)
{
PRAT hno = nullptr;
NumObjSetIntValue( &hno, 10 );
powrat( &hno, *phno, radix, precision);
NumObjAssign( phno, hno );
NumObjDestroy( &hno );
}
void NumObjNot(PRAT *phno, bool fIntegerMode, PRAT chopNum, uint32_t radix, int32_t precision)
{
if (radix == 10 && !fIntegerMode)
{
intrat( phno, radix, precision);
addrat( phno, rat_one, precision);
NumObjNegate( phno );
}
else
{
xorrat( phno, chopNum, radix, precision);
}
}
void NumObjSin(PRAT *phno, ANGLE_TYPE angletype, uint32_t radix, int32_t precision )
{
sinanglerat(phno, angletype, radix, precision);
}
void NumObjCos(PRAT *phno, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
cosanglerat(phno, angletype, radix, precision);
}
void NumObjTan(PRAT *phno, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
tananglerat(phno, angletype, radix, precision);
}
/******************************************************************\
*
* Number format conversion routines
*
* History:
* 06-Dec-1996 wrote them
\******************************************************************/
void NumObjSetIntValue(PRAT *phnol, LONG i )
{
PRAT pr = nullptr;
pr = longtorat( i );
NumObjAssign( phnol, pr );
destroyrat(pr);
}
void NumObjSetIUlongValue(PRAT *phnol, ULONG ul )
{
PRAT pr = nullptr;
pr = Ulongtorat( ul );
NumObjAssign( phnol, pr );
destroyrat(pr);
}
// A Set with 64 bit number
void NumObjSetUlonglongValue(PRAT *phnol, ULONGLONG ul, uint32_t radix, int32_t precision)
{
ULONG hi, lo;
PRAT hno = nullptr;
hi = HIDWORD(ul);
lo = LODWORD(ul);
NumObjSetIUlongValue(phnol, hi);
NumObjSetIntValue(&hno, 32);
lshrat(phnol, hno, radix, precision);
NumObjSetIUlongValue(&hno, lo);
orrat(phnol, hno, radix, precision);
NumObjDestroy(&hno);
}
ULONGLONG NumObjGetUlValue( PRAT hnol, uint32_t radix, int32_t precision)
{
return rattoUlonglong(hnol, radix, precision);
}
wstring NumObjToString(PRAT hnoNum, uint32_t radix, NUMOBJ_FMT fmt, int32_t precision)
{
return RatToString(hnoNum, fmt, radix, precision);
}
bool NumObjIsZero(PRAT hno)
{
return zerrat(hno);
}
bool NumObjIsLess(PRAT hno1, PRAT hno2, int32_t precision)
{
return rat_lt(hno1, hno2, precision);
}
bool NumObjIsLessEq(PRAT hno1, PRAT hno2, int32_t precision)
{
return rat_le(hno1, hno2, precision);
}
bool NumObjIsGreaterEq(PRAT hno1, PRAT hno2, int32_t precision)
{
return rat_ge(hno1, hno2, precision);
}
bool NumObjIsEq(PRAT hno1, PRAT hno2, int32_t precision)
{
return rat_equ(hno1, hno2, precision);
}
void NumObjAssign(PRAT *phnol, PRAT hnor)
{
DUPRAT(*phnol, hnor);
}
int32_t NumObjGetExp(PRAT hno)
{
return LOGRATRADIX(hno);
}
void NumObjDestroy(PRAT *phno)
{
destroyrat(*phno);
}

View File

@@ -0,0 +1,201 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Header Files/CalcEngine.h"
using namespace CalcEngine;
// Routines to perform standard operations &|^~<<>>+-/*% and pwr.
CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rational const& lhs, CalcEngine::Rational const& rhs)
{
PRAT rhsRat = rhs.ToPRAT();
PRAT hno = nullptr;
// Remove any variance in how 0 could be represented in rat e.g. -0, 0/n, etc.
PRAT resultRat = nullptr;
if (lhs.IsZero())
{
NumObjAssign(&resultRat, rat_zero);
}
else
{
resultRat = lhs.ToPRAT();
}
try
{
switch (operation)
{
case IDC_AND:
andrat(&resultRat, rhsRat, m_radix, m_precision);
break;
case IDC_OR:
orrat(&resultRat, rhsRat, m_radix, m_precision);
break;
case IDC_XOR:
xorrat(&resultRat, rhsRat, m_radix, m_precision);
break;
case IDC_RSHF:
{
if (m_fIntegerMode)
{
NumObjSetIntValue(&hno, m_dwWordBitWidth);
if (NumObjIsGreaterEq(resultRat, hno, m_precision)) // Lsh/Rsh >= than current word size is always 0
{
throw CALC_E_NORESULT;
}
}
NumObjAssign(&hno, resultRat);
NumObjAssign(&resultRat, rhsRat);
uint64_t w64Bits = NumObjGetUlValue(resultRat, m_radix, m_precision);
bool fMsb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1;
rshrat(&resultRat, hno, m_radix, m_precision);
if (fMsb)
{
intrat(&resultRat, m_radix, m_precision);
PRAT temp = m_chopNumbers[m_numwidth].ToPRAT();
PRAT chopRat = m_chopNumbers[m_numwidth].ToPRAT();
rshrat(&temp, hno, m_radix, m_precision);
intrat(&temp, m_radix, m_precision);
xorrat(&temp, chopRat, m_radix, m_precision);
orrat(&resultRat, temp, m_radix, m_precision);
destroyrat(temp);
destroyrat(chopRat);
}
break;
}
case IDC_LSHF:
if (m_fIntegerMode)
{
NumObjSetIntValue(&hno, m_dwWordBitWidth);
if (NumObjIsGreaterEq(resultRat, hno, m_precision))
{
throw CALC_E_NORESULT;
}
}
NumObjAssign(&hno, resultRat);
NumObjAssign(&resultRat, rhsRat);
lshrat(&resultRat, hno, m_radix, m_precision);
break;
case IDC_ADD:
addrat(&resultRat, rhsRat, m_precision);
break;
case IDC_SUB:
// in order to do ( rhsRat - resultRat ) we actually do -(resultRat - rhsRat ) because it's quicker
subrat(&resultRat, rhsRat, m_precision);
NumObjNegate(&resultRat);
break;
case IDC_MUL:
mulrat(&resultRat, rhsRat, m_precision);
break;
case IDC_DIV:
case IDC_MOD:
{
int iNumeratorSign = 1, iDenominatorSign = 1, iFinalSign = 1;
// REVIEW: These lengthly number assignments can be replaced with some quick pointer swaps.
// the swaps cannot change the value of pratX unless we also modify the code that calls
// the DoOperation function.
NumObjAssign(&hno, resultRat);
NumObjAssign(&resultRat, rhsRat);
if(m_fIntegerMode)
{
uint64_t w64Bits = NumObjGetUlValue(resultRat, m_radix, m_precision);
bool fMsb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1;
if (fMsb)
{
PRAT chopRat = m_chopNumbers[m_numwidth].ToPRAT();
NumObjNot(&resultRat, true, chopRat, m_radix, m_precision);
destroyrat(chopRat);
addrat(&resultRat, rat_one, m_precision);
iNumeratorSign = -1;
}
w64Bits = NumObjGetUlValue(hno, m_radix, m_precision);
fMsb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1;
if (fMsb)
{
PRAT chopRat = m_chopNumbers[m_numwidth].ToPRAT();
NumObjNot( &hno, true, chopRat, m_radix, m_precision);
destroyrat(chopRat);
addrat( &hno, rat_one, m_precision);
iDenominatorSign = -1;
}
}
if (operation == IDC_DIV)
{
iFinalSign = iNumeratorSign * iDenominatorSign;
divrat(&resultRat, hno, m_precision);
}
else
{
iFinalSign = iNumeratorSign;
modrat(&resultRat, hno );
}
if (m_fIntegerMode && iFinalSign == -1)
{
intrat(&resultRat, m_radix, m_precision);
NumObjNegate(&resultRat);
}
break;
}
case IDC_PWR: // Calculates rhsRat to the resultRat(th) power.
{
NumObjAssign(&hno, resultRat);
NumObjAssign(&resultRat, rhsRat);
powrat(&resultRat, hno , m_radix, m_precision);
break;
}
case IDC_ROOT: // Calculates rhsRat to the resultRat(th) root.
{
NumObjAssign(&hno, resultRat);
NumObjAssign(&resultRat, rhsRat);
rootrat(&resultRat, hno, m_radix, m_precision);
break;
}
}
}
catch (DWORD dwErrCode)
{
DisplayError(dwErrCode);
// On error, return the original value
destroyrat(resultRat);
resultRat = lhs.ToPRAT();
}
destroyrat(hno);
destroyrat(rhsRat);
Rational result{ resultRat };
destroyrat(resultRat);
return result;
}

View File

@@ -0,0 +1,213 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**************************************************************************/
/*** SCICALC Scientific Calculator for Windows 3.00.12 ***/
/*** (c)1989 Microsoft Corporation. All Rights Reserved. ***/
/*** ***/
/*** sciset.c ***/
/*** ***/
/*** Functions contained: ***/
/*** ***/
/*** Functions called: ***/
/*** none ***/
/*** ***/
/*** History: ***/
/*** 12-Dec-1996 Added SetMaxIntDigits ***/
/*** Whenever-97 Removed SetMaxIntDigits ***/
/*** ***/
/**************************************************************************/
#include "pch.h"
#include "Header Files/CalcEngine.h"
using namespace CalcEngine;
//
// To be called when either the radix or num width changes. You can use -1 in either of these values to mean
// dont change that.
void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwidth)
{
// When in integer mode, the number is represented in 2's complement form. When a bit width is changing, we can
// change the number representation back to sign, abs num form in ratpak. Soon when display sees this, it will
// convert to 2's complement form, but this time all high bits will be propogated. Eg. -127, in byte mode is
// represented as 1000,0001. This puts it back as sign=-1, 01111111 . But DisplayNum will see this and convert it
// back to 1111,1111,1000,0001 when in Word mode.
if (m_fIntegerMode)
{
PRAT curRat = m_currentVal.ToPRAT();
ULONGLONG w64Bits = NumObjGetUlValue(curRat, m_radix, m_precision);
bool fMsb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1; // make sure you use the old width
if (fMsb)
{
// If high bit is set, then get the decimal number in -ve 2'scompl form.
PRAT chopRat = m_chopNumbers[m_numwidth].ToPRAT();
NumObjNot(&curRat, true, chopRat, m_radix, m_precision);
destroyrat(chopRat);
addrat(&curRat, rat_one, m_precision);
NumObjNegate(&curRat);
m_currentVal = Rational{ curRat };
}
destroyrat(curRat);
}
if (radixtype >= HEX_RADIX && radixtype <= BIN_RADIX)
{
m_radix = NRadixFromRadixType(radixtype);
// radixtype is not even saved
}
if (numwidth >= QWORD_WIDTH && numwidth <= BYTE_WIDTH)
{
m_numwidth = numwidth;
m_dwWordBitWidth = DwWordBitWidthFromeNumWidth(numwidth);
}
// inform ratpak that a change in base or precision has occured
BaseOrPrecisionChanged();
// display the correct number for the new state (ie convert displayed
// number to correct base)
DisplayNum();
}
LONG CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH /*numwidth*/)
{
static constexpr int nBitMax[] = { 64, 32, 16, 8 };
LONG wmax = nBitMax[0];
if (m_numwidth >= 0 && m_numwidth < ARRAYSIZE(nBitMax))
{
wmax = nBitMax[m_numwidth];
}
return wmax;
}
uint32_t CCalcEngine::NRadixFromRadixType( RADIX_TYPE radixtype)
{
static constexpr uint32_t rgnRadish[4]={16, 10, 8, 2}; /* Number bases in the same order as radixtype */
uint32_t radix = 10;
// convert special bases into symbolic values
if (radixtype >= 0 && radixtype < ARRAYSIZE(rgnRadish))
{
radix = rgnRadish[radixtype];
}
return radix;
}
// Toggles a given bit into the number representation. returns true if it changed it actually.
bool CCalcEngine::TryToggleBit(CalcEngine::Rational& rat, DWORD wbitno)
{
DWORD wmax = DwWordBitWidthFromeNumWidth(m_numwidth);
if (wbitno >= wmax)
{
return false; // ignore error cant happen
}
PRAT hnumPow = nullptr;
NumObjAssign(&hnumPow, rat_two);
PRAT hnum = longtorat(wbitno);
powrat(&hnumPow, hnum, m_radix, m_precision);
PRAT resultRat = rat.ToPRAT();
intrat(&resultRat, m_radix, m_precision);
if (NumObjIsZero(resultRat))
{
// This is the same work around happenning in SciCalcFunctions. Ought to move to intrat function itself.
// Basic bug is there which doesn treat 0/ n as 0, or -0 as 0 etc.
NumObjAssign(&resultRat, rat_zero);
}
xorrat(&resultRat, hnumPow, m_radix, m_precision);
rat = Rational{ resultRat };
destroyrat(resultRat);
NumObjDestroy(&hnumPow);
NumObjDestroy(&hnum);
return true;
}
// Returns the nearest power of two
int CCalcEngine::QuickLog2(int iNum)
{
int iRes = 0;
// while first digit is a zero
while (!(iNum & 1))
{
iRes++;
iNum >>= 1;
}
// if our number isn't a perfect square
iNum = iNum >> 1;
if (iNum)
{
// find the largest digit
for (iNum = iNum >> 1; iNum; iNum = iNum >> 1)
++iRes;
// and then add two
iRes += 2;
}
return iRes;
}
////////////////////////////////////////////////////////////////////////
//
// UpdateMaxIntDigits
//
// determine the maximum number of digits needed for the current precision,
// word size, and base. This number is conservative towards the small side
// such that there may be some extra bits left over. For example, base 8 requires 3 bits per digit.
// A word size of 32 bits allows for 10 digits with a remainder of two bits. Bases
// that require variable numnber of bits (non-power-of-two bases) are approximated
// by the next highest power-of-two base (again, to be conservative and gaurentee
// there will be no over flow verse the current word size for numbers entered).
// Base 10 is a special case and always uses the base 10 precision (m_nPrecisionSav).
void CCalcEngine::UpdateMaxIntDigits()
{
if (m_radix == 10)
{
// if in integer mode you still have to honor the max digits you can enter based on bit width
if (m_fIntegerMode)
{
m_cIntDigitsSav = static_cast<int>(m_maxDecimalValueStrings[m_numwidth].length()) - 1;
// This is the max digits you can enter a decimal in fixed width mode aka integer mode -1. The last digit
// has to be checked separately
}
else
{
m_cIntDigitsSav = m_precision;
}
}
else
{
m_cIntDigitsSav = m_dwWordBitWidth / CCalcEngine::QuickLog2(m_radix);
}
}
void CCalcEngine::ChangeBaseConstants(uint32_t radix, int maxIntDigits, int32_t precision)
{
if (10 == radix)
{
ChangeConstants(radix, precision); // Base 10 precesion for internal computing still needs to be 32, to
// take care of decimals preceisly. For eg. to get the HI word of a qword, we do a rsh, which depends on getting
// 18446744073709551615 / 4294967296 = 4294967295.9999917... This is important it works this and doesnt reduce
// the precision to number of digits allowed to enter. In otherwords precision and # of allowed digits to be
// entered are different.
}
else
{
ChangeConstants(radix, maxIntDigits + 1);
}
}
void CCalcEngine::BaseOrPrecisionChanged()
{
UpdateMaxIntDigits();
CCalcEngine::ChangeBaseConstants(m_radix, m_cIntDigitsSav, m_precision);
}