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);
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
class CalcException : std::exception
{
public:
CalcException(HRESULT hr)
{
m_hr = hr;
}
HRESULT GetException()
{
return m_hr;
}
private:
HRESULT m_hr;
};
void IFT(HRESULT hr)
{
if (FAILED(hr))
{
CalcException exception(hr);
throw(exception);
}
}

View File

@@ -0,0 +1,335 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{311e866d-8b93-4609-a691-265941fee101}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<ProjectName>CalcManager</ProjectName>
<RootNamespace>CalcManager</RootNamespace>
<DisableAppLocalVCLibs>false</DisableAppLocalVCLibs>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.17763.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<!-- This has to be exactly in this place for this to work -->
<PropertyGroup>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>/Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="CalcException.h" />
<ClInclude Include="CalculatorHistory.h" />
<ClInclude Include="CalculatorManager.h" />
<ClInclude Include="CalculatorResource.h" />
<ClInclude Include="CalculatorVector.h" />
<ClInclude Include="Command.h" />
<ClInclude Include="ExpressionCommand.h" />
<ClInclude Include="ExpressionCommandInterface.h" />
<ClInclude Include="Header Files\CalcEngine.h" />
<ClInclude Include="Header Files\CalcUtils.h" />
<ClInclude Include="Header Files\CCommand.h" />
<ClInclude Include="Header Files\EngineStrings.h" />
<ClInclude Include="Header Files\History.h" />
<ClInclude Include="Header Files\ICalcDisplay.h" />
<ClInclude Include="Header Files\CalcInput.h" />
<ClInclude Include="Header Files\IHistoryDisplay.h" />
<ClInclude Include="Header Files\Number.h" />
<ClInclude Include="Header Files\Rational.h" />
<ClInclude Include="Header Files\scimath.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Ratpack\CalcErr.h" />
<ClInclude Include="Ratpack\ratconst.h" />
<ClInclude Include="Ratpack\ratpak.h" />
<ClInclude Include="UnitConverter.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="CalculatorHistory.Cpp" />
<ClCompile Include="CalculatorManager.cpp" />
<ClCompile Include="CEngine\calc.cpp" />
<ClCompile Include="CEngine\CalcUtils.cpp" />
<ClCompile Include="CEngine\History.cpp" />
<ClCompile Include="CEngine\CalcInput.cpp" />
<ClCompile Include="CEngine\Number.cpp" />
<ClCompile Include="CEngine\Rational.cpp" />
<ClCompile Include="CEngine\scicomm.cpp" />
<ClCompile Include="CEngine\scidisp.cpp" />
<ClCompile Include="CEngine\scifunc.cpp" />
<ClCompile Include="CEngine\scimath.cpp" />
<ClCompile Include="CEngine\scioper.cpp" />
<ClCompile Include="CEngine\sciset.cpp" />
<ClCompile Include="ExpressionCommand.cpp" />
<ClCompile Include="Ratpack\basex.cpp" />
<ClCompile Include="Ratpack\conv.cpp" />
<ClCompile Include="Ratpack\exp.cpp" />
<ClCompile Include="Ratpack\fact.cpp" />
<ClCompile Include="Ratpack\itrans.cpp" />
<ClCompile Include="Ratpack\itransh.cpp" />
<ClCompile Include="Ratpack\logic.cpp" />
<ClCompile Include="Ratpack\num.cpp" />
<ClCompile Include="Ratpack\rat.cpp" />
<ClCompile Include="Ratpack\support.cpp" />
<ClCompile Include="Ratpack\trans.cpp" />
<ClCompile Include="Ratpack\transh.cpp" />
<!-- <ClCompile Include="Source Files\CalculatorController.cpp" />
<ClCompile Include="Source Files\CalculatorHistory.Cpp" />
<ClCompile Include="Source Files\CalculatorManager.cpp" /> -->
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="UnitConverter.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@@ -0,0 +1,169 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="CEngine">
<UniqueIdentifier>{957a8e3c-00c7-48bc-b63c-83b2140a8251}</UniqueIdentifier>
</Filter>
<Filter Include="RatPack">
<UniqueIdentifier>{a1bae6f0-0a01-447d-9a3a-5c65bcd384e6}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{5149465e-c5c9-48a2-b676-f11380b733a0}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="ExpressionCommand.cpp" />
<ClCompile Include="CEngine\calc.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\CalcUtils.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\History.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\scicomm.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\scidisp.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\scifunc.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\scimath.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\scioper.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\sciset.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="Ratpack\basex.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\conv.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\exp.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\fact.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\itrans.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\itransh.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\logic.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\num.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\rat.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\support.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\trans.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="Ratpack\transh.cpp">
<Filter>RatPack</Filter>
</ClCompile>
<ClCompile Include="CalculatorHistory.Cpp" />
<ClCompile Include="CalculatorManager.cpp" />
<ClCompile Include="UnitConverter.cpp" />
<ClCompile Include="CEngine\CalcInput.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\Number.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="CEngine\Rational.cpp">
<Filter>CEngine</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Command.h" />
<ClInclude Include="ExpressionCommand.h" />
<ClInclude Include="ExpressionCommandInterface.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Header Files\History.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\CalcEngine.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\CalcUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\CCommand.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\EngineStrings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\scimath.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Ratpack\CalcErr.h">
<Filter>RatPack</Filter>
</ClInclude>
<ClInclude Include="Ratpack\ratconst.h">
<Filter>RatPack</Filter>
</ClInclude>
<ClInclude Include="Ratpack\ratpak.h">
<Filter>RatPack</Filter>
</ClInclude>
<ClInclude Include="CalcException.h" />
<ClInclude Include="CalculatorVector.h" />
<ClInclude Include="Header Files\CalcEngine.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\CalcUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\CCommand.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\EngineStrings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\History.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\scimath.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UnitConverter.h" />
<ClInclude Include="CalculatorHistory.h" />
<ClInclude Include="CalculatorManager.h" />
<ClInclude Include="CalculatorResource.h" />
<ClInclude Include="Header Files\ICalcDisplay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\CalcInput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\IHistoryDisplay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\Number.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Header Files\Rational.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "CalculatorHistory.h"
using namespace std;
using namespace CalculationManager;
CalculatorHistory::CalculatorHistory(CALCULATOR_MODE eMode, size_t maxSize) :
m_mode(eMode),
m_maxHistorySize(maxSize)
{}
unsigned int CalculatorHistory::AddToHistory(_In_ shared_ptr<CalculatorVector <pair<wstring, int>>> const &tokens, _In_ shared_ptr<CalculatorVector<shared_ptr<IExpressionCommand>>> const &commands, _In_ wstring_view result)
{
unsigned int addedIndex;
wstring generatedExpression;
shared_ptr<HISTORYITEM> spHistoryItem = make_shared<HISTORYITEM>();
spHistoryItem->historyItemVector.spTokens = tokens;
spHistoryItem->historyItemVector.spCommands = commands;
// to be changed when pszexp is back
tokens->GetString(&generatedExpression);
// Prefixing and suffixing the special Unicode markers to ensure that the expression
// in the history doesn't get broken for RTL languages
spHistoryItem->historyItemVector.expression = L'\u202d' + generatedExpression + L'\u202c';
spHistoryItem->historyItemVector.result = wstring(result);
addedIndex = AddItem(spHistoryItem);
return addedIndex;
}
unsigned int CalculatorHistory::AddItem(_In_ shared_ptr<HISTORYITEM> const &spHistoryItem)
{
int lastIndex;
if (m_historyItems.size() >= m_maxHistorySize)
{
m_historyItems.erase(m_historyItems.begin());
}
m_historyItems.push_back(spHistoryItem);
lastIndex = static_cast<unsigned>(m_historyItems.size() - 1);
return lastIndex;
}
bool CalculatorHistory::RemoveItem(_In_ unsigned int uIdx)
{
if (uIdx > m_historyItems.size() - 1)
{
return false;
}
m_historyItems.erase(m_historyItems.begin() + uIdx);
return true;
}
vector<shared_ptr<HISTORYITEM>> const& CalculatorHistory::GetHistory()
{
return m_historyItems;
}
shared_ptr<HISTORYITEM> const& CalculatorHistory::GetHistoryItem(_In_ unsigned int uIdx)
{
assert(uIdx >= 0 && uIdx < m_historyItems.size());
return m_historyItems.at(uIdx);
}
CalculatorHistory::~CalculatorHistory(void)
{
ClearHistory();
}
void CalculatorHistory::ClearHistory()
{
m_historyItems.clear();
}

View File

@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "ExpressionCommandInterface.h"
#include "Header Files/IHistoryDisplay.h"
namespace CalculationManager
{
enum CALCULATOR_MODE
{
CM_STD = 0,
CM_SCI,
};
struct HISTORYITEMVECTOR
{
std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> spTokens;
std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> spCommands;
std::wstring expression;
std::wstring result;
};
struct HISTORYITEM
{
HISTORYITEMVECTOR historyItemVector;
};
class CalculatorHistory :
public IHistoryDisplay
{
public:
CalculatorHistory(CALCULATOR_MODE eMode, const size_t maxSize);
unsigned int AddToHistory(_In_ std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> const &spTokens, _In_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &spCommands, std::wstring_view result);
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistory();
std::shared_ptr<HISTORYITEM> const& GetHistoryItem(unsigned int uIdx);
void ClearHistory();
unsigned int AddItem(_In_ std::shared_ptr<HISTORYITEM> const &spHistoryItem);
bool RemoveItem(unsigned int uIdx);
const size_t MaxHistorySize() const { return m_maxHistorySize; }
~CalculatorHistory(void);
private:
std::vector<std::shared_ptr<HISTORYITEM>> m_historyItems;
CALCULATOR_MODE m_mode;
const size_t m_maxHistorySize;
};
}

View File

@@ -0,0 +1,867 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Header Files\CalcEngine.h"
#include "CalculatorManager.h"
#include "CalculatorResource.h"
using namespace std;
using namespace CalcEngine;
static constexpr size_t MAX_HISTORY_ITEMS = 20;
static constexpr size_t SERIALIZED_NUMBER_MINSIZE = 3;
// Converts Memory Command enum value to unsigned char,
// while ignoring Warning C4309: 'conversion' : truncation of constant value
#define MEMORY_COMMAND_TO_UNSIGNED_CHAR(c)\
__pragma(warning(push))\
__pragma(warning(disable: 4309))\
static_cast<unsigned char>(c)\
__pragma(warning(pop))
namespace CalculationManager
{
CalculatorManager::CalculatorManager(_In_ ICalcDisplay* displayCallback, _In_ IResourceProvider* resourceProvider) :
m_displayCallback(displayCallback),
m_resourceProvider(resourceProvider),
m_currentDegreeMode(Command::CommandNULL),
m_savedDegreeMode(Command::CommandDEG),
m_isExponentialFormat(false),
m_persistedPrimaryValue(),
m_currentCalculatorEngine(nullptr),
m_pStdHistory(new CalculatorHistory(CM_STD, MAX_HISTORY_ITEMS)),
m_pSciHistory(new CalculatorHistory(CM_SCI, MAX_HISTORY_ITEMS)),
m_inHistoryItemLoadMode(false)
{
CCalcEngine::InitialOneTimeOnlySetup(*m_resourceProvider);
}
/// <summary>
/// Destructor for CalculatorManager
/// Ends two CCalcEngine
/// </summary>
CalculatorManager::~CalculatorManager()
{
this->MemorizedNumberClearAll();
}
/// <summary>
/// Call the callback function using passed in IDisplayHelper.
/// Used to set the primary display value on ViewModel
/// </summary>
/// <param name="text">wstring representing text to be displayed</param>
void CalculatorManager::SetPrimaryDisplay(_In_ const wstring& displayString, _In_ bool isError)
{
if (!m_inHistoryItemLoadMode)
{
m_displayCallback->SetPrimaryDisplay(displayString, isError);
}
}
void CalculatorManager::SetIsInError(bool isError)
{
m_displayCallback->SetIsInError(isError);
}
void CalculatorManager::DisplayPasteError()
{
m_currentCalculatorEngine->DisplayError(CALC_E_DOMAIN /*code for "Invalid input" error*/);
}
void CalculatorManager::MaxDigitsReached()
{
m_displayCallback->MaxDigitsReached();
}
void CalculatorManager::BinaryOperatorReceived()
{
m_displayCallback->BinaryOperatorReceived();
}
void CalculatorManager::MemoryItemChanged(unsigned int indexOfMemory)
{
m_displayCallback->MemoryItemChanged(indexOfMemory);
}
/// <summary>
/// Call the callback function using passed in IDisplayHelper.
/// Used to set the expression display value on ViewModel
/// </summary>
/// <param name="expressionString">wstring representing expression to be displayed</param>
void CalculatorManager::SetExpressionDisplay(_Inout_ shared_ptr<CalculatorVector<pair<wstring, int>>> const &tokens, _Inout_ shared_ptr<CalculatorVector<shared_ptr<IExpressionCommand>>> const &commands)
{
if (!m_inHistoryItemLoadMode)
{
m_displayCallback->SetExpressionDisplay(tokens, commands);
}
}
/// <summary>
/// Callback from the CalculatorControll
/// Passed in string representations of memorized numbers get passed to the client
/// </summary>
/// <param name="memorizedNumber">vector containing wstring values of memorized numbers</param>
void CalculatorManager::SetMemorizedNumbers(_In_ const vector<wstring>& memorizedNumbers)
{
m_displayCallback->SetMemorizedNumbers(memorizedNumbers);
}
/// <summary>
/// Callback from the engine
/// Used to set the current unmatched open parenthesis count
/// </summary>
/// <param name="parenthesisCount">string containing the parenthesis count</param>
void CalculatorManager::SetParenDisplayText(const wstring& parenthesisCount)
{
m_displayCallback->SetParenDisplayText(parenthesisCount);
}
/// <summary>
/// Reset CalculatorManager.
/// Set the mode to the standard calculator
/// Set the degree mode as regular degree (as oppose to Rad or Grad)
/// Clear all the entries and memories
/// Clear Memory if clearMemory parameter is true.(Default value is true)
/// </summary>
void CalculatorManager::Reset(bool clearMemory /* = true*/)
{
m_savedCommands.clear();
SetStandardMode();
if (m_scientificCalculatorEngine)
{
m_scientificCalculatorEngine->ProcessCommand(IDC_DEG);
m_scientificCalculatorEngine->ProcessCommand(IDC_CLEAR);
if (m_isExponentialFormat)
{
m_isExponentialFormat = false;
m_scientificCalculatorEngine->ProcessCommand(IDC_FE);
}
}
if (m_programmerCalculatorEngine)
{
m_programmerCalculatorEngine->ProcessCommand(IDC_CLEAR);
}
if (clearMemory)
{
this->MemorizedNumberClearAll();
}
}
/// <summary>
/// Change the current calculator engine to standard calculator engine.
/// </summary>
void CalculatorManager::SetStandardMode()
{
if (!m_standardCalculatorEngine)
{
m_standardCalculatorEngine = make_unique<CCalcEngine>(false /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pStdHistory);
}
m_currentCalculatorEngine = m_standardCalculatorEngine.get();
m_currentCalculatorEngine->ProcessCommand(IDC_DEC);
m_currentCalculatorEngine->ProcessCommand(IDC_CLEAR);
m_currentCalculatorEngine->ChangePrecision(static_cast<int>(CalculatorPrecision::StandardModePrecision));
UpdateMaxIntDigits();
m_pHistory = m_pStdHistory.get();
}
/// <summary>
/// Change the current calculator engine to scientific calculator engine.
/// </summary>
void CalculatorManager::SetScientificMode()
{
if (!m_scientificCalculatorEngine)
{
m_scientificCalculatorEngine = make_unique<CCalcEngine>(true /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pSciHistory);
}
m_currentCalculatorEngine = m_scientificCalculatorEngine.get();
m_currentCalculatorEngine->ProcessCommand(IDC_DEC);
m_currentCalculatorEngine->ProcessCommand(IDC_CLEAR);
m_currentCalculatorEngine->ChangePrecision(static_cast<int>(CalculatorPrecision::ScientificModePrecision));
m_pHistory = m_pSciHistory.get();
}
/// <summary>
/// Change the current calculator engine to scientific calculator engine.
/// </summary>
void CalculatorManager::SetProgrammerMode()
{
if(!m_programmerCalculatorEngine)
{
m_programmerCalculatorEngine = make_unique<CCalcEngine>(true /* Respect Order of Operations */, true /* Set to Integer Mode */, m_resourceProvider, this, nullptr);
}
m_currentCalculatorEngine = m_programmerCalculatorEngine.get();
m_currentCalculatorEngine->ProcessCommand(IDC_DEC);
m_currentCalculatorEngine->ProcessCommand(IDC_CLEAR);
m_currentCalculatorEngine->ChangePrecision(static_cast<int>(CalculatorPrecision::ProgrammerModePrecision));
}
/// <summary>
/// Send command to the Calc Engine
/// Cast Command Enum to WPARAM.
/// Handle special commands such as mode change and combination of two commands.
/// </summary>
/// <param name="command">Enum Command</command>
void CalculatorManager::SendCommand(_In_ Command command)
{
// When the expression line is cleared, we save the current state, which includes,
// primary display, memory, and degree mode
if (command == Command::CommandCLEAR || command == Command::CommandEQU
|| command == Command::ModeBasic || command == Command::ModeScientific || command == Command::ModeProgrammer)
{
switch (command)
{
case Command::ModeBasic:
this->SetStandardMode();
break;
case Command::ModeScientific:
this->SetScientificMode();
break;
case Command::ModeProgrammer:
this->SetProgrammerMode();
break;
default:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(command));
}
m_savedCommands.clear(); // Clear the previous command history
if (command != Command::CommandEQU && command != Command::CommandCLEAR)
{
m_savedCommands.push_back(MapCommandForSerialize(command));
}
this->SerializePrimaryDisplay();
this->SerializeMemory();
m_savedDegreeMode = m_currentDegreeMode;
return;
}
if (command == Command::CommandDEG || command == Command::CommandRAD || command == Command::CommandGRAD)
{
m_currentDegreeMode = command;
}
if (command != Command::CommandFE)
{
m_savedCommands.push_back(MapCommandForSerialize(command)); // Save the commands in the m_savedCommands
}
switch (command)
{
case Command::CommandASIN:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandINV));
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandSIN));
break;
case Command::CommandACOS:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandINV));
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandCOS));
break;
case Command::CommandATAN:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandINV));
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandTAN));
break;
case Command::CommandPOWE:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandINV));
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandLN));
break;
case Command::CommandASINH:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandINV));
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandSINH));
break;
case Command::CommandACOSH:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandINV));
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandCOSH));
break;
case Command::CommandATANH:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandINV));
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(Command::CommandTANH));
break;
case Command::CommandFE:
m_isExponentialFormat = !m_isExponentialFormat;
// fall through
default:
m_currentCalculatorEngine->ProcessCommand(static_cast<WPARAM>(command));
break;
}
}
/// <summary>
/// Convert Command to unsigned char.
/// Since some Commands are higher than 255, they are saved after subtracting 255
/// The smallest Command is CommandSIGN = 80, thus, subtracted value does not overlap with other values.
/// </summary>
/// <param name="command">Enum Command</command>
unsigned char CalculatorManager::MapCommandForSerialize(Command command)
{
unsigned int commandToSave = static_cast<unsigned int>(command);
commandToSave > UCHAR_MAX ? commandToSave -= UCHAR_MAX : commandToSave;
return static_cast<unsigned char>(commandToSave);
}
/// <summary>
/// Convert Command to unsigned int
/// The command that is smaller than 80, CommandSIGN, can be converted back to original value by adding 255.
/// </summary>
/// <param name="command">unsigned char value represent the saved command</command>
unsigned int CalculatorManager::MapCommandForDeSerialize(unsigned char command)
{
unsigned int commandToLoad = command;
if (command < static_cast<unsigned int>(Command::CommandSIGN))
{
commandToLoad += UCHAR_MAX;
}
return commandToLoad;
}
/// <summary>
/// Return saved degree mode which is saved when last time the expression was cleared.
/// </summary>
Command CalculatorManager::SerializeSavedDegreeMode()
{
return m_savedDegreeMode;
}
void CalculatorManager::SerializePrimaryDisplay()
{
m_savedPrimaryValue.clear();
m_currentCalculatorEngine->ProcessCommand(IDC_STORE);
auto memoryObject = m_currentCalculatorEngine->PersistedMemObject();
if (memoryObject != nullptr)
{
m_savedPrimaryValue = SerializeRational(*memoryObject);
}
}
/// <summary>
/// Return serialized primary display that is saved when the expression line was cleared.
/// </summary>
vector<long> CalculatorManager::GetSerializedPrimaryDisplay()
{
return m_savedPrimaryValue;
}
/// <summary>
/// DeSerialize the primary display from vector of long
/// </summary>
/// <param name = "serializedPrimaryDisplay">Serialized Rational of primary display</param>
void CalculatorManager::DeSerializePrimaryDisplay(const vector<long> &serializedPrimaryDisplay)
{
if (serializedPrimaryDisplay.size() == 0)
{
return;
}
m_persistedPrimaryValue = DeSerializeRational(serializedPrimaryDisplay.begin());
this->LoadPersistedPrimaryValue();
}
/// <summary>
/// Load the persisted value that is saved in memory of CalcEngine
/// </summary>
void CalculatorManager::LoadPersistedPrimaryValue()
{
m_currentCalculatorEngine->PersistedMemObject(m_persistedPrimaryValue);
m_currentCalculatorEngine->ProcessCommand(IDC_RECALL);
}
/// <summary>
/// Serialize the Memory to vector of long
/// </summary>
/// <return type = "std::vector<long>">Serialized Rational of memory</return>
void CalculatorManager::SerializeMemory()
{
m_serializedMemory.clear();
for (auto const& memoryItem : m_memorizedNumbers)
{
auto serialMem = SerializeRational(memoryItem);
m_serializedMemory.insert(m_serializedMemory.end(), serialMem.begin(), serialMem.end());
}
}
vector<long> CalculatorManager::GetSerializedMemory()
{
return m_serializedMemory;
}
/// <summary>
/// DeSerialize the Memory from vector of long
/// </summary>
/// <param name = "serializedMemory">Serialized Rational of memory</param>
void CalculatorManager::DeSerializeMemory(const vector<long> &serializedMemory)
{
vector<long>::const_iterator itr = serializedMemory.begin();
while (itr != serializedMemory.end())
{
Rational memoryItem = DeSerializeRational(itr);
auto lengthMemoryItem = (2 * SERIALIZED_NUMBER_MINSIZE) + memoryItem.P().Mantissa().size() + memoryItem.Q().Mantissa().size();
m_memorizedNumbers.push_back(memoryItem);
itr += lengthMemoryItem;
}
this->SetMemorizedNumbersString();
}
/// <summary>
/// Return the commands saved since the expression has been cleared.
/// </summary>
vector<unsigned char> CalculatorManager::SerializeCommands()
{
return m_savedCommands;
}
/// <summary>
/// Replay the serialized commands
/// </summary>
/// <param name = "serializedData">Serialized commands</param>
void CalculatorManager::DeSerializeCommands(_In_ const vector<unsigned char>& serializedData)
{
m_savedCommands.clear();
for (auto commandItr = serializedData.begin(); commandItr != serializedData.end(); ++commandItr)
{
if (*commandItr >= MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber) &&
*commandItr <= MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizedNumberClearAll))
{
//MemoryCommands(which have values above 255) are pushed on m_savedCommands upon casting to unsigned char.
//SerializeCommands uses m_savedCommands, which is then used in DeSerializeCommnds.
//Hence, a simple cast to MemoryCommand is not sufficient.
MemoryCommand memoryCommand = static_cast<MemoryCommand>(*commandItr + UCHAR_MAX + 1);
unsigned int indexOfMemory = 0;
switch (memoryCommand)
{
case MemoryCommand::MemorizeNumber:
this->MemorizeNumber();
break;
case MemoryCommand::MemorizedNumberLoad:
if (commandItr + 1 == serializedData.end())
{
throw out_of_range("Expecting index of memory, data ended prematurely");
}
indexOfMemory = *(++commandItr);
this->MemorizedNumberLoad(indexOfMemory);
break;
case MemoryCommand::MemorizedNumberAdd:
if (commandItr + 1 == serializedData.end())
{
throw out_of_range("Expecting index of memory, data ended prematurely");
}
indexOfMemory = *(++commandItr);
this->MemorizedNumberAdd(indexOfMemory);
break;
case MemoryCommand::MemorizedNumberSubtract:
if (commandItr + 1 == serializedData.end())
{
throw out_of_range("Expecting index of memory, data ended prematurely");
}
indexOfMemory = *(++commandItr);
this->MemorizedNumberSubtract(indexOfMemory);
break;
case MemoryCommand::MemorizedNumberClearAll:
this->MemorizedNumberClearAll();
break;
default:
break;
}
}
else
{
this->SendCommand(static_cast<Command>(MapCommandForDeSerialize(*commandItr)));
}
}
}
/// <summary>
/// Memorize the current displayed value
/// Notify the client with new the new memorize value vector
/// </summary>
void CalculatorManager::MemorizeNumber()
{
m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber));
if (!(m_currentCalculatorEngine->FInErrorState()))
{
m_currentCalculatorEngine->ProcessCommand(IDC_STORE);
auto memoryObjectPtr = m_currentCalculatorEngine->PersistedMemObject();
if (memoryObjectPtr != nullptr)
{
m_memorizedNumbers.insert(m_memorizedNumbers.begin(), *memoryObjectPtr);
}
if (m_memorizedNumbers.size() > m_maximumMemorySize)
{
m_memorizedNumbers.resize(m_maximumMemorySize);
}
this->SetMemorizedNumbersString();
}
}
/// <summary>
/// Recall the memorized number.
/// The memorized number gets loaded to the primary display
/// </summary>
/// <param name="indexOfMemeory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberLoad, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
{
this->MemorizedNumberSelect(indexOfMemory);
m_currentCalculatorEngine->ProcessCommand(IDC_RECALL);
}
}
/// <summary>
/// Do the addition to the selected memory
/// It adds primary display value to the selected memory
/// Notify the client with new the new memorize value vector
/// </summary>
/// <param name="indexOfMemeory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberAdd, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
{
if (m_memorizedNumbers.empty())
{
this->MemorizeNumber();
}
else
{
this->MemorizedNumberSelect(indexOfMemory);
m_currentCalculatorEngine->ProcessCommand(IDC_MPLUS);
this->MemorizedNumberChanged(indexOfMemory);
this->SetMemorizedNumbersString();
}
m_displayCallback->MemoryItemChanged(indexOfMemory);
}
}
void CalculatorManager::MemorizedNumberClear(_In_ unsigned int indexOfMemory)
{
if (indexOfMemory < m_memorizedNumbers.size())
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberClear, indexOfMemory);
m_memorizedNumbers.erase(m_memorizedNumbers.begin() + indexOfMemory);
}
}
/// <summary>
/// Do the subtraction to the selected memory
/// It adds primary display value to the selected memory
/// Notify the client with new the new memorize value vector
/// </summary>
/// <param name="indexOfMemeory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberSubtract, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
{
// To add negative of the number on display to the memory -x = x - 2x
if (m_memorizedNumbers.empty())
{
this->MemorizeNumber();
this->MemorizedNumberSubtract(0);
this->MemorizedNumberSubtract(0);
}
else
{
this->MemorizedNumberSelect(indexOfMemory);
m_currentCalculatorEngine->ProcessCommand(IDC_MMINUS);
this->MemorizedNumberChanged(indexOfMemory);
this->SetMemorizedNumbersString();
}
m_displayCallback->MemoryItemChanged(indexOfMemory);
}
}
/// <summary>
/// Clear all the memorized values
/// Notify the client with new the new memorize value vector
/// </summary>
void CalculatorManager::MemorizedNumberClearAll()
{
m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizedNumberClearAll));
m_memorizedNumbers.clear();
m_currentCalculatorEngine->ProcessCommand(IDC_MCLEAR);
this->SetMemorizedNumbersString();
}
/// <summary>
/// Helper function that selects a memeory from the vector and set it to CCalcEngine
/// Saved RAT number needs to be copied and passed in, as CCalcEngine destoried the passed in RAT
/// </summary>
/// <param name="indexOfMemeory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberSelect(_In_ unsigned int indexOfMemory)
{
if (!(m_currentCalculatorEngine->FInErrorState()))
{
auto memoryObject = m_memorizedNumbers.at(indexOfMemory);
m_currentCalculatorEngine->PersistedMemObject(memoryObject);
}
}
/// <summary>
/// Helper function that needs to be executed when memory is modified
/// When memory is modified, destory the old RAT and put the new RAT in vector
/// </summary>
/// <param name="indexOfMemeory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberChanged(_In_ unsigned int indexOfMemory)
{
if (!(m_currentCalculatorEngine->FInErrorState()))
{
auto memoryObject = m_currentCalculatorEngine->PersistedMemObject();
if (memoryObject != nullptr)
{
m_memorizedNumbers.at(indexOfMemory) = *memoryObject;
}
}
}
void CalculatorManager::SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory)
{
m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(command));
if (indexOfMemory > UCHAR_MAX)
{
throw invalid_argument("Unexpected value. IndexOfMemory is bigger than the biggest unsigned char");
}
m_savedCommands.push_back(static_cast<unsigned char>(indexOfMemory));
}
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems()
{
return m_pHistory->GetHistory();
}
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems(_In_ CALCULATOR_MODE mode)
{
return (mode == CM_STD) ?
m_pStdHistory->GetHistory() :
m_pSciHistory->GetHistory();
}
shared_ptr<HISTORYITEM> const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx)
{
return m_pHistory->GetHistoryItem(uIdx);
}
void CalculatorManager::OnHistoryItemAdded(_In_ unsigned int addedItemIndex)
{
m_displayCallback->OnHistoryItemAdded(addedItemIndex);
}
bool CalculatorManager::RemoveHistoryItem(_In_ unsigned int uIdx)
{
return m_pHistory->RemoveItem(uIdx);
}
void CalculatorManager::ClearHistory()
{
m_pHistory->ClearHistory();
}
void CalculatorManager::SetRadix(RADIX_TYPE iRadixType)
{
switch (iRadixType)
{
case RADIX_TYPE::HEX_RADIX:
m_currentCalculatorEngine->ProcessCommand(IDC_HEX);
break;
case RADIX_TYPE::DEC_RADIX:
m_currentCalculatorEngine->ProcessCommand(IDC_DEC);
break;
case RADIX_TYPE::OCT_RADIX:
m_currentCalculatorEngine->ProcessCommand(IDC_OCT);
break;
case RADIX_TYPE::BIN_RADIX:
m_currentCalculatorEngine->ProcessCommand(IDC_BIN);
break;
default:
break;
}
SetMemorizedNumbersString();
}
void CalculatorManager::SetMemorizedNumbersString()
{
vector<wstring> resultVector;
for (auto const& memoryItem : m_memorizedNumbers)
{
int radix = m_currentCalculatorEngine->GetCurrentRadix();
wstring stringValue = m_currentCalculatorEngine->GetStringForDisplay(memoryItem, radix);
if (!stringValue.empty())
{
resultVector.push_back(m_currentCalculatorEngine->GroupDigitsPerRadix(stringValue, radix));
}
}
m_displayCallback->SetMemorizedNumbers(resultVector);
}
CalculationManager::Command CalculatorManager::GetCurrentDegreeMode()
{
if (m_currentDegreeMode == Command::CommandNULL)
{
m_currentDegreeMode = Command::CommandDEG;
}
return m_currentDegreeMode;
}
void CalculatorManager::SetHistory(_In_ CALCULATOR_MODE eMode, _In_ vector<shared_ptr<HISTORYITEM>> const& history)
{
CalculatorHistory* pHistory = nullptr;
switch (eMode)
{
case CM_STD:
pHistory = m_pStdHistory.get();
break;
case CM_SCI:
pHistory = m_pSciHistory.get();
break;
}
if (pHistory)
{
pHistory->ClearHistory();
for (unsigned int i = 0; i < history.size(); ++i)
{
pHistory->AddItem(history[i]);
}
}
}
wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision)
{
return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision) : L"";
}
void CalculatorManager::SetPrecision(int32_t precision)
{
m_currentCalculatorEngine->ChangePrecision(precision);
}
void CalculatorManager::UpdateMaxIntDigits()
{
m_currentCalculatorEngine->UpdateMaxIntDigits();
}
wchar_t CalculatorManager::DecimalSeparator()
{
return m_currentCalculatorEngine ? m_currentCalculatorEngine->DecimalSeparator() : m_resourceProvider->GetCEngineString(L"sDecimal")[0];
}
bool CalculatorManager::IsEngineRecording()
{
return m_currentCalculatorEngine->FInRecordingState() ? true : false;
}
void CalculatorManager::SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode)
{
m_inHistoryItemLoadMode = isHistoryItemLoadMode;
}
/// <summary>
/// Serialize Rational to vector of long
/// How Rational is serialized :
/// Serialized Rational.P(Number) + Serialized Rational.Q(Number)
/// How Number is saved :
/// [0] = Rational.P.Sign
/// [1] = Rational.P.Mantissa.size
/// [2] = Rational.P.Exp
/// [3] = Rational.P.Mantissa[0]
/// [4] = Rational.P.Mantissa[1]
/// ...
/// [2 + Rational.P.Mantissa.size] = Rational.P.Mantissa[size - 1]
/// </summary>
/// <param name = "rat">Rational number to be serialized</param>
vector<long> CalculatorManager::SerializeRational(Rational const& rat)
{
vector<long> serializedRational{};
auto serialP = SerializeNumber(rat.P());
serializedRational.insert(serializedRational.end(), serialP.begin(), serialP.end());
auto serialQ = SerializeNumber(rat.Q());
serializedRational.insert(serializedRational.end(), serialQ.begin(), serialQ.end());
return serializedRational;
}
/// <summary>
/// DeserializeRational vector and construct a Rational
/// How Rational is serialized :
/// Serialized Rational.P(Number) + Serialized Rational.Q(Number)
/// </summary>
Rational CalculatorManager::DeSerializeRational(vector<long>::const_iterator itr)
{
auto p = DeSerializeNumber(itr);
auto q = DeSerializeNumber(itr + SERIALIZED_NUMBER_MINSIZE + p.Mantissa().size());
return Rational(p, q);
}
/// <summary>
/// Serialize Number to vector of long
/// How Number is saved :
/// [0] = Number.Sign
/// [1] = Number.Mantissa.size
/// [2] = Number.Exp
/// [3] = Number.Mantissa[0]
/// [4] = Number.Mantissa[1]
/// ...
/// [2 + Number.Mantissa.size] = Number.Mantissa[size - 1]
/// </summary>
/// <param name = "num">Number to be serialized</param>
vector<long> CalculatorManager::SerializeNumber(Number const& num)
{
vector<long> serializedNumber{};
serializedNumber.push_back(num.Sign());
serializedNumber.push_back(static_cast<long>(num.Mantissa().size()));
serializedNumber.push_back(num.Exp());
for (auto const& digit : num.Mantissa())
{
serializedNumber.push_back(digit);
}
return serializedNumber;
}
/// <summary>
/// DeserializeNumber vector and construct a Number
/// How Number is saved :
/// [0] = Number.Sign
/// [1] = Number.Mantissa.size
/// [2] = Number.Exp
/// [3] = Number.Mantissa[0]
/// [4] = Number.Mantissa[1]
/// ...
/// [2 + Number.Mantissa.size] = Number.Mantissa[size - 1]
/// </summary>
Number CalculatorManager::DeSerializeNumber(vector<long>::const_iterator itr)
{
int32_t sign = *itr;
uint32_t size = *(itr + 1);
int32_t exp = *(itr + 2);
vector<uint32_t> mant{};
for (size_t i = 0; i < size; ++i)
{
mant.emplace_back(*(itr + 3 + i));
}
return Number{ sign, exp, mant };
}
}

View File

@@ -0,0 +1,145 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "CalculatorHistory.h"
#include "Header Files\Rational.h"
namespace CalculationManager
{
enum class Command;
struct HISTORYITEM;
enum class CalculatorMode
{
StandardMode,
ScientificMode,
ProgrammerMode,
};
enum class CalculatorPrecision
{
StandardModePrecision = 16,
ScientificModePrecision = 32,
ProgrammerModePrecision = 64
};
// Numbering continues from the Enum Command from Command.h
// with some gap to ensure there is no overlap of these ids
// when static_cast<unsigned char> is performed on these ids
// they shouldn't fall in any number range greater than 80. So never
// make the memory command ids go below 330
enum class MemoryCommand
{
MemorizeNumber = 330,
MemorizedNumberLoad = 331,
MemorizedNumberAdd = 332,
MemorizedNumberSubtract = 333,
MemorizedNumberClearAll = 334,
MemorizedNumberClear = 335
};
class CalculatorManager sealed : public virtual ICalcDisplay
{
private:
ICalcDisplay* const m_displayCallback;
CCalcEngine* m_currentCalculatorEngine;
std::unique_ptr<CCalcEngine> m_scientificCalculatorEngine;
std::unique_ptr<CCalcEngine> m_standardCalculatorEngine;
std::unique_ptr<CCalcEngine> m_programmerCalculatorEngine;
IResourceProvider* const m_resourceProvider;
bool m_inHistoryItemLoadMode;
std::vector<CalcEngine::Rational> m_memorizedNumbers;
CalcEngine::Rational m_persistedPrimaryValue;
bool m_isExponentialFormat;
static const unsigned int m_maximumMemorySize = 100;
// For persistance
std::vector<unsigned char> m_savedCommands;
std::vector<long> m_savedPrimaryValue;
std::vector<long> m_serializedMemory;
std::vector<long> m_currentSerializedMemory;
Command m_currentDegreeMode;
Command m_savedDegreeMode;
unsigned char MapCommandForSerialize(Command command);
unsigned int MapCommandForDeSerialize(unsigned char command);
void SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory);
void MemorizedNumberSelect(_In_ unsigned int);
void MemorizedNumberChanged(_In_ unsigned int);
void LoadPersistedPrimaryValue();
static std::vector<long> SerializeRational(CalcEngine::Rational const& rat);
static CalcEngine::Rational DeSerializeRational(std::vector<long>::const_iterator itr);
static std::vector<long> SerializeNumber(CalcEngine::Number const& num);
static CalcEngine::Number DeSerializeNumber(std::vector<long>::const_iterator itr);
std::shared_ptr<CalculatorHistory> m_pStdHistory;
std::shared_ptr<CalculatorHistory> m_pSciHistory;
CalculatorHistory* m_pHistory;
public:
// ICalcDisplay
void SetPrimaryDisplay(_In_ const std::wstring& displayString, _In_ bool isError) override;
void SetIsInError(bool isError) override;
void SetExpressionDisplay(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens, _Inout_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &commands) override;
void SetMemorizedNumbers(_In_ const std::vector<std::wstring>& memorizedNumbers) override;
void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) override;
void SetParenDisplayText(const std::wstring& parenthesisCount);
void DisplayPasteError();
void MaxDigitsReached() override;
void BinaryOperatorReceived() override;
void MemoryItemChanged(unsigned int indexOfMemory) override;
CalculatorManager(ICalcDisplay* displayCallback, IResourceProvider* resourceProvider);
~CalculatorManager();
void Reset(bool clearMemory = true);
void SetStandardMode();
void SetScientificMode();
void SetProgrammerMode();
void SendCommand(_In_ Command command);
std::vector<unsigned char> SerializeCommands();
void DeSerializeCommands(_In_ const std::vector<unsigned char>& serializedData);
void SerializeMemory();
std::vector<long> GetSerializedMemory();
void DeSerializeMemory(const std::vector<long> &serializedMemory);
void SerializePrimaryDisplay();
std::vector<long> GetSerializedPrimaryDisplay();
void DeSerializePrimaryDisplay(const std::vector<long> &serializedPrimaryDisplay);
Command SerializeSavedDegreeMode();
void MemorizeNumber();
void MemorizedNumberLoad(_In_ unsigned int);
void MemorizedNumberAdd(_In_ unsigned int);
void MemorizedNumberSubtract(_In_ unsigned int);
void MemorizedNumberClear(_In_ unsigned int);
void MemorizedNumberClearAll();
bool IsEngineRecording();
std::vector<unsigned char> GetSavedCommands(){ return m_savedCommands; }
void SetRadix(RADIX_TYPE iRadixType);
void SetMemorizedNumbersString();
std::wstring GetResultForRadix(uint32_t radix, int32_t precision);
void SetPrecision(int32_t precision);
void UpdateMaxIntDigits();
wchar_t DecimalSeparator();
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems();
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems(_In_ CalculationManager::CALCULATOR_MODE mode);
std::shared_ptr<HISTORYITEM> const& GetHistoryItem(_In_ unsigned int uIdx);
bool RemoveHistoryItem(_In_ unsigned int uIdx);
void ClearHistory();
const size_t MaxHistorySize() const { return m_pHistory->MaxHistorySize(); }
CalculationManager::Command GetCurrentDegreeMode();
void SetHistory(_In_ CALCULATOR_MODE eMode, _In_ std::vector<std::shared_ptr<HISTORYITEM>> const& history);
void SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode);
};
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
namespace CalculationManager
{
class IResourceProvider
{
public:
virtual ~IResourceProvider() { }
// Should return a string from the resource table for strings used
// by the calculation engine. The strings that must be defined
// and the ids to define them with can be seen in EngineStrings.h
// with SIDS prefix. Additionally it must provide values for string
// ids "sDecimal", "sThousand" and "sGrouping". See
// http://technet.microsoft.com/en-us/library/cc782655(v=ws.10).aspx
// for what these values refer to.
virtual std::wstring GetCEngineString(const std::wstring& id) = 0;
};
}

View File

@@ -0,0 +1,155 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
template <typename TType>
class CalculatorVector
{
public:
HRESULT GetAt(_In_opt_ unsigned int index, _Out_ TType *item)
{
HRESULT hr = S_OK;
try
{
*item = m_vector.at(index);
}
catch (std::out_of_range /*ex*/)
{
hr = E_BOUNDS;
}
return hr;
}
HRESULT GetSize(_Out_ unsigned int *size)
{
*size = static_cast<unsigned>(m_vector.size());
return S_OK;
}
HRESULT SetAt(_In_ unsigned int index, _In_opt_ TType item)
{
HRESULT hr = S_OK;
try
{
m_vector[index] = item;
}
catch (std::out_of_range /*ex*/)
{
hr = E_BOUNDS;
}
return hr;
}
HRESULT RemoveAt(_In_ unsigned int index)
{
HRESULT hr = S_OK;
if (index < m_vector.size())
{
m_vector.erase(m_vector.begin() + index);
}
else
{
hr = E_BOUNDS;
}
return hr;
}
HRESULT InsertAt(_In_ unsigned int index, _In_ TType item)
{
HRESULT hr = S_OK;
try
{
auto iter = m_vector.begin() + index;
m_vector.insert(iter, item);
}
catch (std::bad_alloc /*ex*/)
{
hr = E_OUTOFMEMORY;
}
return hr;
}
HRESULT Truncate(_In_ unsigned int index)
{
HRESULT hr = S_OK;
if (index < m_vector.size())
{
auto startIter = m_vector.begin() + index;
m_vector.erase(startIter, m_vector.end());
}
else
{
hr = E_BOUNDS;
}
return hr;
}
HRESULT Append(_In_opt_ TType item)
{
HRESULT hr = S_OK;
try
{
m_vector.push_back(item);
}
catch (std::bad_alloc /*ex*/)
{
hr = E_OUTOFMEMORY;
}
return hr;
}
HRESULT RemoveAtEnd()
{
m_vector.erase(--(m_vector.end()));
return S_OK;
}
HRESULT Clear()
{
m_vector.clear();
return S_OK;
}
HRESULT GetString(_Out_ std::wstring* expression)
{
HRESULT hr = S_OK;
unsigned int nTokens = 0;
std::pair <std::wstring, int> currentPair;
hr = this->GetSize(&nTokens);
if (SUCCEEDED(hr))
{
for (unsigned int i = 0; i < nTokens; i++)
{
hr = this->GetAt(i, &currentPair);
if (SUCCEEDED(hr))
{
expression->append(currentPair.first);
if (i != (nTokens - 1))
{
expression->append(L" ");
}
}
}
std::wstring expressionSuffix{};
hr = GetExpressionSuffix(&expressionSuffix);
if (SUCCEEDED(hr))
{
expression->append(expressionSuffix);
}
}
return hr;
}
HRESULT GetExpressionSuffix(_Out_ std::wstring* suffix)
{
*suffix = L" =";
return S_OK;
}
private:
std::vector<TType> m_vector;
};

227
src/CalcManager/Command.h Normal file
View File

@@ -0,0 +1,227 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
namespace UnitConversionManager
{
enum class Command
{
Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine,
Decimal,
Negate, Backspace,
Clear,
Reset,
None
};
}
namespace CurrencyConversionManager
{
enum class Command
{
Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine,
Decimal,
Negate, Backspace,
Clear,
None
};
}
namespace CalculationManager
{
enum class CommandType
{
UnaryCommand,
BinaryCommand,
OperandCommand,
Parentheses
};
enum class Command
{
// Commands for programmer calculators are omitted.
CommandDEG = 321,
CommandRAD = 322,
CommandGRAD = 323,
CommandDegrees = 324,
CommandHYP = 325,
CommandNULL = 0,
// No new command should not be added before CommandSign, 80
// If it is needed, the following two functions need to be revised too.
// CalculatorManager::MapCommandForSerialize(Command command);
// CalculatorManager::MapCommandForDeSerialize(unsigned char command);
CommandSIGN = 80,
CommandCLEAR = 81,
CommandCENTR = 82,
CommandBACK = 83,
CommandPNT = 84,
// Hole 85
// Unused commands defined in Command.h is omitted.
CommandXor = 88,
CommandLSHF = 89,
CommandRSHF = 90,
CommandDIV = 91,
CommandMUL = 92,
CommandADD = 93,
CommandSUB = 94,
CommandMOD = 95,
CommandROOT = 96,
CommandPWR = 97,
CommandCHOP = 98, // Unary operators must be between CommandCHOP and CommandEQU
CommandROL = 99,
CommandROR = 100,
CommandCOM = 101,
CommandSIN = 102,
CommandCOS = 103,
CommandTAN = 104,
CommandSINH = 105,
CommandCOSH = 106,
CommandTANH = 107,
CommandLN = 108,
CommandLOG = 109,
CommandSQRT = 110,
CommandSQR = 111,
CommandCUB = 112,
CommandFAC = 113,
CommandREC = 114,
CommandDMS = 115,
CommandCUBEROOT = 116, //x ^ 1/3
CommandPOW10 = 117, // 10 ^ x
CommandPERCENT = 118,
CommandFE = 119,
CommandPI = 120,
CommandEQU = 121,
CommandMCLEAR = 122,
CommandRECALL = 123,
CommandSTORE = 124,
CommandMPLUS = 125,
CommandMMINUS = 126,
CommandEXP = 127,
CommandOPENP = 128,
CommandCLOSEP = 129,
Command0 = 130, // The controls for 0 through F must be consecutive and in order
Command1 = 131,
Command2 = 132,
Command3 = 133,
Command4 = 134,
Command5 = 135,
Command6 = 136,
Command7 = 137,
Command8 = 138,
Command9 = 139,
CommandA = 140,
CommandB = 141,
CommandC = 142,
CommandD = 143,
CommandE = 144,
CommandF = 145, // this is last control ID which must match the string table
CommandINV = 146,
CommandSET_RESULT = 147,
CommandAnd = 86,
CommandOR = 87,
CommandNot = 101,
ModeBasic = 200,
ModeScientific = 201,
CommandASIN = 202,
CommandACOS = 203,
CommandATAN = 204,
CommandPOWE = 205,
CommandASINH = 206,
CommandACOSH = 207,
CommandATANH = 208,
ModeProgrammer = 209,
CommandHex = 313,
CommandDec = 314,
CommandOct = 315,
CommandBin = 316,
CommandQword = 317,
CommandDword = 318,
CommandWord = 319,
CommandByte = 320,
CommandBINEDITSTART = 700,
CommandBINPOS0 = 700,
CommandBINPOS1 = 701,
CommandBINPOS2 = 702,
CommandBINPOS3 = 703,
CommandBINPOS4 = 704,
CommandBINPOS5 = 705,
CommandBINPOS6 = 706,
CommandBINPOS7 = 707,
CommandBINPOS8 = 708,
CommandBINPOS9 = 709,
CommandBINPOS10 = 710,
CommandBINPOS11 = 711,
CommandBINPOS12 = 712,
CommandBINPOS13 = 713,
CommandBINPOS14 = 714,
CommandBINPOS15 = 715,
CommandBINPOS16 = 716,
CommandBINPOS17 = 717,
CommandBINPOS18 = 718,
CommandBINPOS19 = 719,
CommandBINPOS20 = 720,
CommandBINPOS21 = 721,
CommandBINPOS22 = 722,
CommandBINPOS23 = 723,
CommandBINPOS24 = 724,
CommandBINPOS25 = 725,
CommandBINPOS26 = 726,
CommandBINPOS27 = 727,
CommandBINPOS28 = 728,
CommandBINPOS29 = 729,
CommandBINPOS30 = 730,
CommandBINPOS31 = 731,
CommandBINPOS32 = 732,
CommandBINPOS33 = 733,
CommandBINPOS34 = 734,
CommandBINPOS35 = 735,
CommandBINPOS36 = 736,
CommandBINPOS37 = 737,
CommandBINPOS38 = 738,
CommandBINPOS39 = 739,
CommandBINPOS40 = 740,
CommandBINPOS41 = 741,
CommandBINPOS42 = 742,
CommandBINPOS43 = 743,
CommandBINPOS44 = 744,
CommandBINPOS45 = 745,
CommandBINPOS46 = 746,
CommandBINPOS47 = 747,
CommandBINPOS48 = 748,
CommandBINPOS49 = 749,
CommandBINPOS50 = 750,
CommandBINPOS51 = 751,
CommandBINPOS52 = 752,
CommandBINPOS53 = 753,
CommandBINPOS54 = 754,
CommandBINPOS55 = 755,
CommandBINPOS56 = 756,
CommandBINPOS57 = 757,
CommandBINPOS58 = 758,
CommandBINPOS59 = 759,
CommandBINPOS60 = 760,
CommandBINPOS61 = 761,
CommandBINPOS62 = 762,
CommandBINPOS63 = 763,
CommandBINEDITEND = 763
};
}

View File

@@ -0,0 +1,315 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Header Files\CCommand.h"
#include "CalculatorVector.h"
#include "ExpressionCommand.h"
using namespace std;
constexpr wchar_t chNegate = L'-';
constexpr wchar_t chExp = L'e';
constexpr wchar_t chPlus = L'+';
CParentheses::CParentheses(_In_ int command) :m_command(command)
{}
int CParentheses::GetCommand() const
{
return m_command;
}
CalculationManager::CommandType CParentheses::GetCommandType() const
{
return CalculationManager::CommandType::Parentheses;
}
void CParentheses::Accept(_In_ ISerializeCommandVisitor &commandVisitor)
{
commandVisitor.Visit(*this);
}
CUnaryCommand::CUnaryCommand(int command)
{
m_command = make_shared<CalculatorVector<int>>();
m_command->Append(command);
}
CUnaryCommand::CUnaryCommand(int command1, int command2)
{
m_command = make_shared<CalculatorVector<int>>();
m_command->Append(command1);
m_command->Append(command2);
}
const shared_ptr<CalculatorVector<int>> & CUnaryCommand::GetCommands() const
{
return m_command;
}
CalculationManager::CommandType CUnaryCommand::GetCommandType() const
{
return CalculationManager::CommandType::UnaryCommand;
}
void CUnaryCommand::SetCommand(int command)
{
m_command->Clear();
m_command->Append(command);
}
void CUnaryCommand::SetCommands(int command1, int command2)
{
m_command->Clear();
m_command->Append(command1);
m_command->Append(command2);
}
void CUnaryCommand::Accept(_In_ ISerializeCommandVisitor &commandVisitor)
{
commandVisitor.Visit(*this);
}
CBinaryCommand::CBinaryCommand(int command) :m_command(command)
{}
void CBinaryCommand::SetCommand(int command)
{
m_command = command;
}
int CBinaryCommand::GetCommand() const
{
return m_command;
}
CalculationManager::CommandType CBinaryCommand::GetCommandType() const
{
return CalculationManager::CommandType::BinaryCommand;
}
void CBinaryCommand::Accept(_In_ ISerializeCommandVisitor &commandVisitor)
{
commandVisitor.Visit(*this);
}
COpndCommand::COpndCommand(_In_ shared_ptr<CalculatorVector<int>> const &commands,
_In_ bool fNegative,
_In_ bool fDecimal,
_In_ bool fSciFmt) :
m_commands(commands), m_fNegative(fNegative), m_fDecimal(fDecimal), m_fSciFmt(fSciFmt)
{
m_hnoNum = nullptr;
}
void COpndCommand::Initialize(_In_ PRAT hNoNum)
{
assert(&m_hnoNum != nullptr);
if (m_hnoNum != nullptr)
{
destroyrat(m_hnoNum);
m_hnoNum = nullptr;
}
DUPRAT(m_hnoNum, hNoNum);
}
const shared_ptr<CalculatorVector<int>> & COpndCommand::GetCommands() const
{
return m_commands;
}
void COpndCommand::SetCommands(shared_ptr<CalculatorVector<int>> const& commands)
{
m_commands = commands;
}
void COpndCommand::AppendCommand(int command)
{
unsigned int nCommands;
m_commands->GetSize(&nCommands);
if (m_fSciFmt)
{
ClearAllAndAppendCommand(static_cast<CalculationManager::Command>(command));
}
else
{
m_commands->Append(command);
}
if (command == IDC_PNT)
{
m_fDecimal = true;
}
}
void COpndCommand::ToggleSign()
{
unsigned int commandCount;
m_commands->GetSize(&commandCount);
for (unsigned int i = 0; i < commandCount; i++)
{
int nOpCode;
m_commands->GetAt(i, &nOpCode);
if (nOpCode != IDC_0)
{
m_fNegative = !m_fNegative;
break;
}
}
}
void COpndCommand::RemoveFromEnd()
{
if (m_fSciFmt)
{
ClearAllAndAppendCommand(CalculationManager::Command::Command0);
}
else
{
unsigned int nCommands;
m_commands->GetSize(&nCommands);
if (nCommands == 1)
{
ClearAllAndAppendCommand(CalculationManager::Command::Command0);
}
else
{
int nOpCode;
m_commands->GetAt(nCommands - 1, &nOpCode);
if (nOpCode == IDC_PNT)
{
m_fDecimal = false;
}
m_commands->RemoveAt(nCommands - 1);
}
}
}
bool COpndCommand::IsNegative() const
{
return m_fNegative;
}
bool COpndCommand::IsSciFmt() const
{
return m_fSciFmt;
}
bool COpndCommand::IsDecimalPresent() const
{
return m_fDecimal;
}
CalculationManager::CommandType COpndCommand::GetCommandType() const
{
return CalculationManager::CommandType::OperandCommand;
}
void COpndCommand::ClearAllAndAppendCommand(CalculationManager::Command command)
{
m_commands->Clear();
m_commands->Append(static_cast<int>(command));
m_fSciFmt = false;
m_fNegative = false;
m_fDecimal = false;
}
const wstring & COpndCommand::GetToken(wchar_t decimalSymbol)
{
static const wchar_t chZero = L'0';
unsigned int nCommands;
m_commands->GetSize(&nCommands);
m_token.clear();
int nOpCode;
for (unsigned int i = 0; i < nCommands; i++)
{
m_commands->GetAt(i, &nOpCode);
if (nOpCode == IDC_PNT)
{
m_token.append(wstring{ decimalSymbol });
}
else if (nOpCode == IDC_EXP)
{
m_token.append(&chExp);
int nextOpCode;
m_commands->GetAt(i + 1, &nextOpCode);
if (nextOpCode != IDC_SIGN)
{
m_token.append(&chPlus);
}
}
else if (nOpCode == IDC_SIGN)
{
m_token.append(&chNegate);
}
else
{
wstring num = to_wstring(nOpCode - IDC_0);
m_token.append(num);
}
}
// Remove zeros
bool fDigitsFound = false;
int trimIdx = 0;
for (unsigned int i = 0; i < m_token.size(); i++)
{
if (m_token.at(i) != chZero)
{
if (m_token.at(i) == decimalSymbol)
{
trimIdx = i - 1;
}
else
{
trimIdx = i;
}
fDigitsFound = true;
break;
}
}
if (fDigitsFound)
{
m_token.erase(0, trimIdx);
if (m_fNegative)
{
m_token.insert(0, &chNegate);
}
}
else
{
m_token.clear();
m_token.append(&chZero);
}
return m_token;
}
wstring COpndCommand::GetString(uint32_t radix, int32_t precision, wchar_t decimalSymbol)
{
wstring numString{};
if (m_hnoNum != nullptr)
{
numString = NumObjToString(m_hnoNum, radix, eNUMOBJ_FMT::FMT_FLOAT, precision);
}
return numString;
}
COpndCommand::~COpndCommand()
{
destroyrat(m_hnoNum);
}
void COpndCommand::Accept(_In_ ISerializeCommandVisitor &commandVisitor)
{
commandVisitor.Visit(*this);
}

View File

@@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "ExpressionCommandInterface.h"
#include "Header Files\CalcEngine.h"
class CParentheses : public IParenthesisCommand
{
public:
CParentheses(_In_ int command);
int GetCommand() const;
CalculationManager::CommandType GetCommandType() const;
void Accept(_In_ ISerializeCommandVisitor &commandVisitor);
private:
int m_command;
};
class CUnaryCommand : public IUnaryCommand
{
public:
CUnaryCommand(int command);
CUnaryCommand(int command1, int command2);
const std::shared_ptr<CalculatorVector<int>> & GetCommands() const;
CalculationManager::CommandType GetCommandType() const;
void SetCommand(int command);
void SetCommands(int command1, int command2);
void Accept(_In_ ISerializeCommandVisitor &commandVisitor);
private:
std::shared_ptr<CalculatorVector<int>> m_command;
};
class CBinaryCommand : public IBinaryCommand
{
public:
CBinaryCommand(int command);
void SetCommand(int command);
int GetCommand() const;
CalculationManager::CommandType GetCommandType() const;
void Accept(_In_ ISerializeCommandVisitor &commandVisitor);
private:
int m_command;
};
class COpndCommand : public IOpndCommand
{
public:
COpndCommand(_In_ std::shared_ptr<CalculatorVector<int>> const &commands,
_In_ bool fNegative,
_In_ bool fDecimal,
_In_ bool fSciFmt);
~COpndCommand();
void Initialize(_In_ PRAT hNoNum);
const std::shared_ptr<CalculatorVector<int>> & GetCommands() const;
void SetCommands(std::shared_ptr<CalculatorVector<int>> const& commands);
void AppendCommand(int command);
void ToggleSign();
void RemoveFromEnd();
bool IsNegative() const;
bool IsSciFmt() const;
bool IsDecimalPresent() const;
const std::wstring & GetToken(wchar_t decimalSymbol);
CalculationManager::CommandType GetCommandType() const;
void Accept(_In_ ISerializeCommandVisitor &commandVisitor);
std::wstring GetString(uint32_t radix, int32_t precision, wchar_t decimalSymbol);
private:
std::shared_ptr<CalculatorVector<int>> m_commands;
bool m_fNegative;
bool m_fSciFmt;
bool m_fDecimal;
std::wstring m_token;
PRAT m_hnoNum;
void ClearAllAndAppendCommand(CalculationManager::Command command);
};
class ISerializeCommandVisitor
{
public:
virtual void Visit(_In_ COpndCommand &opndCmd) = 0;
virtual void Visit(_In_ CUnaryCommand &unaryCmd) = 0;
virtual void Visit(_In_ CBinaryCommand &binaryCmd) = 0;
virtual void Visit(_In_ CParentheses &paraCmd) = 0;
};

View File

@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "CalculatorVector.h"
#include "Command.h"
class ISerializeCommandVisitor;
class IExpressionCommand
{
public:
virtual CalculationManager::CommandType GetCommandType() const = 0;
virtual void Accept(_In_ ISerializeCommandVisitor &commandVisitor) = 0;
};
class IOperatorCommand : public IExpressionCommand
{
public:
virtual void SetCommand(int command) = 0;
};
class IUnaryCommand : public IOperatorCommand
{
public:
virtual const std::shared_ptr<CalculatorVector<int>> & GetCommands() const = 0;
virtual void SetCommands(int command1, int command2) = 0;
};
class IBinaryCommand : public IOperatorCommand
{
public:
virtual void SetCommand(int command) = 0;
virtual int GetCommand() const = 0;
};
class IOpndCommand : public IExpressionCommand
{
public:
virtual const std::shared_ptr<CalculatorVector<int>> & GetCommands() const= 0;
virtual void AppendCommand(int command) = 0;
virtual void ToggleSign() = 0;
virtual void RemoveFromEnd() = 0;
virtual bool IsNegative() const = 0;
virtual bool IsSciFmt() const = 0;
virtual bool IsDecimalPresent() const = 0;
virtual const std::wstring & GetToken(wchar_t decimalSymbol) = 0;
virtual void SetCommands(std::shared_ptr<CalculatorVector<int>> const& commands) = 0;
};
class IParenthesisCommand : public IExpressionCommand
{
public:
virtual int GetCommand() const = 0;
};

View File

@@ -0,0 +1,214 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/****************************Module*Header***********************************
* Module Name: CCommand.h
*
* Module Descripton:
* Resource ID's for the Engine Commands exposed.
*
* Warnings:
*
* Created: 13-Feb-2008
*
\****************************************************************************/
// The following are the valid id's which can be passed to CCalcEngine::ProcessCommand
#define IDM_HEX 313
#define IDM_DEC 314
#define IDM_OCT 315
#define IDM_BIN 316
#define IDM_QWORD 317
#define IDM_DWORD 318
#define IDM_WORD 319
#define IDM_BYTE 320
#define IDM_DEG 321
#define IDM_RAD 322
#define IDM_GRAD 323
#define IDM_DEGREES 324
#define IDC_HEX IDM_HEX
#define IDC_DEC IDM_DEC
#define IDC_OCT IDM_OCT
#define IDC_BIN IDM_BIN
#define IDC_DEG IDM_DEG
#define IDC_RAD IDM_RAD
#define IDC_GRAD IDM_GRAD
#define IDC_DEGREES IDM_DEGREES
#define IDC_QWORD IDM_QWORD
#define IDC_DWORD IDM_DWORD
#define IDC_WORD IDM_WORD
#define IDC_BYTE IDM_BYTE
// Key IDs:
// These id's must be consecutive from IDC_FIRSTCONTROL to IDC_LASTCONTROL.
// The actual values don't matter but the order and sequence are very important.
// Also, the order of the controls must match the order of the control names
// in the string table.
// For example you want to declare the color for the control IDC_ST_AVE
// Find the string id for that control from the rc file
// Now define the control's id as IDC_FRISTCONTROL+stringID(IDC_ST_AVE)
#define IDC_FIRSTCONTROL IDC_SIGN
#define IDC_SIGN 80
#define IDC_CLEAR 81
#define IDC_CENTR 82
#define IDC_BACK 83
#define IDC_PNT 84
// Hole 85
#define IDC_AND 86 // Binary operators must be between IDC_AND and IDC_PWR
#define IDC_OR 87
#define IDC_XOR 88
#define IDC_LSHF 89
#define IDC_RSHF 90
#define IDC_DIV 91
#define IDC_MUL 92
#define IDC_ADD 93
#define IDC_SUB 94
#define IDC_MOD 95
#define IDC_ROOT 96
#define IDC_PWR 97
#define IDC_UNARYFIRST IDC_CHOP
#define IDC_CHOP 98 // Unary operators must be between IDC_CHOP and IDC_EQU
#define IDC_ROL 99
#define IDC_ROR 100
#define IDC_COM 101
#define IDC_SIN 102
#define IDC_COS 103
#define IDC_TAN 104
#define IDC_SINH 105
#define IDC_COSH 106
#define IDC_TANH 107
#define IDC_LN 108
#define IDC_LOG 109
#define IDC_SQRT 110
#define IDC_SQR 111
#define IDC_CUB 112
#define IDC_FAC 113
#define IDC_REC 114
#define IDC_DMS 115
#define IDC_CUBEROOT 116 //x ^ 1/3
#define IDC_POW10 117 // 10 ^ x
#define IDC_PERCENT 118
#define IDC_UNARYLAST IDC_PERCENT
#define IDC_FE 119
#define IDC_PI 120
#define IDC_EQU 121
#define IDC_MCLEAR 122
#define IDC_RECALL 123
#define IDC_STORE 124
#define IDC_MPLUS 125
#define IDC_MMINUS 126
#define IDC_EXP 127
#define IDC_OPENP 128
#define IDC_CLOSEP 129
#define IDC_0 130 // The controls for 0 through F must be consecutive and in order
#define IDC_1 131
#define IDC_2 132
#define IDC_3 133
#define IDC_4 134
#define IDC_5 135
#define IDC_6 136
#define IDC_7 137
#define IDC_8 138
#define IDC_9 139
#define IDC_A 140
#define IDC_B 141
#define IDC_C 142
#define IDC_D 143
#define IDC_E 144
#define IDC_F 145 // this is last control ID which must match the string table
#define IDC_INV 146
#define IDC_SET_RESULT 147
#define IDC_LASTCONTROL IDC_SET_RESULT
#define IDC_BINEDITSTART 700
#define IDC_BINPOS0 700
#define IDC_BINPOS1 701
#define IDC_BINPOS2 702
#define IDC_BINPOS3 703
#define IDC_BINPOS4 704
#define IDC_BINPOS5 705
#define IDC_BINPOS6 706
#define IDC_BINPOS7 707
#define IDC_BINPOS8 708
#define IDC_BINPOS9 709
#define IDC_BINPOS10 710
#define IDC_BINPOS11 711
#define IDC_BINPOS12 712
#define IDC_BINPOS13 713
#define IDC_BINPOS14 714
#define IDC_BINPOS15 715
#define IDC_BINPOS16 716
#define IDC_BINPOS17 717
#define IDC_BINPOS18 718
#define IDC_BINPOS19 719
#define IDC_BINPOS20 720
#define IDC_BINPOS21 721
#define IDC_BINPOS22 722
#define IDC_BINPOS23 723
#define IDC_BINPOS24 724
#define IDC_BINPOS25 725
#define IDC_BINPOS26 726
#define IDC_BINPOS27 727
#define IDC_BINPOS28 728
#define IDC_BINPOS29 729
#define IDC_BINPOS30 730
#define IDC_BINPOS31 731
#define IDC_BINPOS32 732
#define IDC_BINPOS33 733
#define IDC_BINPOS34 734
#define IDC_BINPOS35 735
#define IDC_BINPOS36 736
#define IDC_BINPOS37 737
#define IDC_BINPOS38 738
#define IDC_BINPOS39 739
#define IDC_BINPOS40 740
#define IDC_BINPOS41 741
#define IDC_BINPOS42 742
#define IDC_BINPOS43 743
#define IDC_BINPOS44 744
#define IDC_BINPOS45 745
#define IDC_BINPOS46 746
#define IDC_BINPOS47 747
#define IDC_BINPOS48 748
#define IDC_BINPOS49 749
#define IDC_BINPOS50 750
#define IDC_BINPOS51 751
#define IDC_BINPOS52 752
#define IDC_BINPOS53 753
#define IDC_BINPOS54 754
#define IDC_BINPOS55 755
#define IDC_BINPOS56 756
#define IDC_BINPOS57 757
#define IDC_BINPOS58 758
#define IDC_BINPOS59 759
#define IDC_BINPOS60 760
#define IDC_BINPOS61 761
#define IDC_BINPOS62 762
#define IDC_BINPOS63 763
#define IDC_BINEDITEND 763
// The strings in the following range IDS_ENGINESTR_FIRST ... IDS_ENGINESTR_MAX are strings allocated in the
// resource for the purpose internal to Engine and cant be used by the clients
#define IDS_ENGINESTR_FIRST 0
#define IDS_ENGINESTR_MAX 200

View File

@@ -0,0 +1,170 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
/****************************Module*Header***********************************\
* Module Name: CalcEngine.h
*
* Module Descripton:
* The class definition for the Calculator's engine class CCalcEngine
*
* Warnings:
*
* Created: 17-Jan-2008
*
\****************************************************************************/
#include "scimath.h"
#include "CCommand.h"
#include "EngineStrings.h"
#include "Command.h"
#include "CalculatorVector.h"
#include "ExpressionCommand.h"
#include "History.h" // for History Collector
#include "CalcInput.h"
#include "ICalcDisplay.h"
#include "Rational.h"
// The following are NOT real exports of CalcEngine, but for forward declarations
// The real exports follows later
// This is expected to be in same order as IDM_QWORD, IDM_DWORD etc.
enum eNUM_WIDTH {
QWORD_WIDTH, // Number width of 64 bits mode (default)
DWORD_WIDTH, // Number width of 32 bits mode
WORD_WIDTH, // Number width of 16 bits mode
BYTE_WIDTH // Number width of 16 bits mode
};
typedef enum eNUM_WIDTH NUM_WIDTH;
static constexpr size_t NUM_WIDTH_LENGTH = 4;
// This is expected to be in same order as IDM_HEX, IDM_DEC, IDM_OCT, IDM_BIN
enum eRADIX_TYPE {
HEX_RADIX,
DEC_RADIX,
OCT_RADIX,
BIN_RADIX
};
typedef enum eRADIX_TYPE RADIX_TYPE;
namespace CalculationManager
{
class IResourceProvider;
}
namespace CalculatorUnitTests
{
class CalcEngineTests;
}
class CCalcEngine {
public:
CCalcEngine(bool fPrecedence, bool fIntegerMode, CalculationManager::IResourceProvider* const pResourceProvider, __in_opt ICalcDisplay *pCalcDisplay, __in_opt std::shared_ptr<IHistoryDisplay> pHistoryDisplay);
void ProcessCommand(WPARAM wID);
void DisplayError (DWORD nError);
std::unique_ptr<CalcEngine::Rational> PersistedMemObject();
void PersistedMemObject(CalcEngine::Rational const& memObject);
bool FInErrorState() { return m_bError; }
bool FInRecordingState() { return m_bRecord; }
void SettingsChanged();
bool IsCurrentTooBigForTrig();
int GetCurrentRadix();
std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision);
void ChangePrecision(int32_t precision) { m_precision = precision; ChangeConstants(m_radix, precision); }
std::wstring GroupDigitsPerRadix(std::wstring_view numberString, uint32_t radix);
std::wstring GetStringForDisplay(CalcEngine::Rational const& rat, uint32_t radix);
void UpdateMaxIntDigits();
wchar_t DecimalSeparator() const;
// Static methods for the instance
static void InitialOneTimeOnlySetup(CalculationManager::IResourceProvider& resourceProvider); // Once per load time to call to intialize all shared global variables
// returns the ptr to string representing the operator. Mostly same as the button, but few special cases for x^y etc.
static std::wstring_view GetString(int ids) { return s_engineStrings[ids]; }
static std::wstring_view OpCodeToString(int nOpCode) { return GetString(IdStrFromCmdId(nOpCode)); }
static std::wstring_view OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype);
private:
bool m_fPrecedence;
bool m_fIntegerMode; /* This is true if engine is explicitly called to be in integer mode. All bases are restricted to be in integers only */
ICalcDisplay *m_pCalcDisplay;
CalculationManager::IResourceProvider* const m_resourceProvider;
int m_nOpCode; /* ID value of operation. */
int m_nPrevOpCode; // opcode which computed the number in m_currentVal. 0 if it is already bracketed or plain number or
// if it hasnt yet been computed
bool m_bChangeOp; /* Flag for changing operation. */
bool m_bRecord; // Global mode: recording or displaying
bool m_bSetCalcState; //Falg for setting teh engine result state
CalcEngine::CalcInput m_input; // Global calc input object for decimal strings
eNUMOBJ_FMT m_nFE; /* Scientific notation conversion flag. */
CalcEngine::Rational m_maxTrigonometricNum;
std::unique_ptr<CalcEngine::Rational> m_memoryValue; // Current memory value.
CalcEngine::Rational m_holdVal; // For holding the second operand in repetitive calculations ( pressing "=" continuously)
CalcEngine::Rational m_currentVal; // Currently displayed number used everywhere.
CalcEngine::Rational m_lastVal; // Number before operation (left operand).
std::array<CalcEngine::Rational, MAXPRECDEPTH> m_parenVals; // Holding array for parenthesis values.
std::array<CalcEngine::Rational, MAXPRECDEPTH> m_precedenceVals; // Holding array for precedence values.
bool m_bError; // Error flag.
bool m_bInv; // Inverse on/off flag.
bool m_bNoPrevEqu; /* Flag for previous equals. */
uint32_t m_radix;
int32_t m_precision;
int m_cIntDigitsSav;
std::vector<uint32_t> m_decGrouping; // Holds the decimal digit grouping number
std::wstring m_numberString;
int m_nTempCom; /* Holding place for the last command. */
int m_openParenCount; // Number of open parentheses.
std::array<int, MAXPRECDEPTH> m_nOp; /* Holding array for parenthesis operations. */
std::array<int, MAXPRECDEPTH> m_nPrecOp; /* Holding array for precedence operations. */
int m_nPrecNum; /* Current number of precedence ops in holding. */
int m_nLastCom; // Last command entered.
ANGLE_TYPE m_angletype; // Current Angle type when in dec mode. one of deg, rad or grad
NUM_WIDTH m_numwidth; // one of qword, dword, word or byte mode.
LONG m_dwWordBitWidth; // # of bits in currently selected word size
CHistoryCollector m_HistoryCollector; // Accumulator of each line of history as various commands are processed
std::array<CalcEngine::Rational, NUM_WIDTH_LENGTH> m_chopNumbers; // word size enforcement
std::array<std::wstring, NUM_WIDTH_LENGTH> m_maxDecimalValueStrings; // maximum values represented by a given word width based off m_chopNumbers
static std::array<std::wstring, CSTRINGSENGMAX> s_engineStrings; // the string table shared across all instances
wchar_t m_decimalSeparator;
wchar_t m_groupSeparator;
private:
void ProcessCommandWorker(WPARAM wParam);
void HandleErrorCommand(WPARAM idc);
void HandleMaxDigitsReached();
void DisplayNum(void);
int IsNumberInvalid(const std::wstring& numberString, int iMaxExp, int iMaxMantissa, uint32_t radix) const;
void DisplayAnnounceBinaryOperator();
void SetPrimaryDisplay(const std::wstring& szText, bool isError = false);
void ClearTemporaryValues();
CalcEngine::Rational TruncateNumForIntMath(CalcEngine::Rational const& rat);
CalcEngine::Rational SciCalcFunctions(CalcEngine::Rational const& rat, DWORD op);
CalcEngine::Rational DoOperation(int operation, CalcEngine::Rational const& lhs, CalcEngine::Rational const& rhs);
void SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwidth);
LONG DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth);
uint32_t NRadixFromRadixType( RADIX_TYPE radixtype);
bool TryToggleBit(CalcEngine::Rational& rat, DWORD wbitno);
void CheckAndAddLastBinOpToHistory(bool addToHistory = true);
int IdcSetAngleTypeDecMode(int idc);
void InitChopNumbers();
static void LoadEngineStrings(CalculationManager::IResourceProvider& resourceProvider);
static int IdStrFromCmdId(int id) { return id - IDC_FIRSTCONTROL + IDS_FIRSTENGSTR; }
static std::vector<uint32_t> DigitGroupingStringToGroupingVector(std::wstring_view groupingString);
std::wstring GroupDigits(std::wstring_view delimiter, std::vector<uint32_t> const& grouping, std::wstring_view displayString, bool isNumNegative = false);
static int QuickLog2(int iNum);
static void ChangeBaseConstants(uint32_t radix, int maxIntDigits, int32_t precision);
void BaseOrPrecisionChanged();
friend class CalculatorUnitTests::CalcEngineTests;
};

View File

@@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "Rational.h"
// Space to hold enough digits for a quadword binary number (64) plus digit separator strings for that number (20)
constexpr int MAX_STRLEN = 84;
namespace CalcEngine
{
class CalcNumSec
{
public:
CalcNumSec() :
m_isNegative(false),
value()
{}
void Clear();
bool IsEmpty() { return value.empty(); }
bool IsNegative() { return m_isNegative; }
void IsNegative(bool value) { m_isNegative = value; }
std::wstring value;
private:
bool m_isNegative;
};
class CalcInput
{
public:
CalcInput() : CalcInput(L'.')
{}
CalcInput(wchar_t decSymbol) :
m_base(),
m_exponent(),
m_hasExponent(false),
m_hasDecimal(false),
m_decPtIndex(0),
m_decSymbol(decSymbol)
{}
void Clear();
bool TryToggleSign(bool isIntegerMode, std::wstring_view maxNumStr);
bool TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMode, std::wstring_view maxNumStr, long wordBitWidth, int maxDigits);
bool TryAddDecimalPt();
bool HasDecimalPt();
bool TryBeginExponent();
void Backspace();
void SetDecimalSymbol(wchar_t decSymbol);
std::wstring ToString(uint32_t radix, bool isIntegerMode);
Rational ToRational(uint32_t radix, int32_t precision);
private:
bool m_hasExponent;
bool m_hasDecimal;
size_t m_decPtIndex;
wchar_t m_decSymbol;
CalcNumSec m_base;
CalcNumSec m_exponent;
};
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
bool IsOpInRange(WPARAM op, uint32_t x, uint32_t y);
bool IsBinOpCode(WPARAM opCode);
// 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);
bool IsDigitOpCode(WPARAM opCode);
bool IsGuiSettingOpCode(WPARAM opCode);

View File

@@ -0,0 +1,339 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/****************************Module*Header***********************************
* Module Name: EngineStrings.h
*
* Module Descripton:
* Resource String ID's for the private strings used by Engine. Internal to Engine related code
* not required by the clients
*
* Warnings:
*
* Created: 13-Feb-2008
*
\****************************************************************************/
#define IDS_FIRSTENGSTR IDS_ENGINESTR_FIRST
#define IDS_DECIMAL 4
// All unary op function names for easy history reading
// This is where the first string after all the commands in order have been placed, should be placed
// keeping in consecutive helps us to allocate 1 string table and index them
#define IDS_FNSZFIRST (IDC_F -IDC_FIRSTCONTROL)+1
#define IDS_FRAC IDS_FNSZFIRST
#define IDS_SIND IDS_FNSZFIRST+1
#define IDS_COSD IDS_FNSZFIRST+2
#define IDS_TAND IDS_FNSZFIRST+3
#define IDS_ASIND IDS_FNSZFIRST+4
#define IDS_ACOSD IDS_FNSZFIRST+5
#define IDS_ATAND IDS_FNSZFIRST+6
#define IDS_SINR IDS_FNSZFIRST+7
#define IDS_COSR IDS_FNSZFIRST+8
#define IDS_TANR IDS_FNSZFIRST+9
#define IDS_ASINR IDS_FNSZFIRST+10
#define IDS_ACOSR IDS_FNSZFIRST+11
#define IDS_ATANR IDS_FNSZFIRST+12
#define IDS_SING IDS_FNSZFIRST+13
#define IDS_COSG IDS_FNSZFIRST+14
#define IDS_TANG IDS_FNSZFIRST+15
#define IDS_ASING IDS_FNSZFIRST+16
#define IDS_ACOSG IDS_FNSZFIRST+17
#define IDS_ATANG IDS_FNSZFIRST+18
#define IDS_ASINH IDS_FNSZFIRST+19
#define IDS_ACOSH IDS_FNSZFIRST+20
#define IDS_ATANH IDS_FNSZFIRST+21
#define IDS_POWE IDS_FNSZFIRST+22
#define IDS_POW10 IDS_FNSZFIRST+23
#define IDS_SQRT IDS_FNSZFIRST+24
#define IDS_SQR IDS_FNSZFIRST+25
#define IDS_CUBE IDS_FNSZFIRST+26
#define IDS_CUBERT IDS_FNSZFIRST+27
#define IDS_FACT IDS_FNSZFIRST+28
#define IDS_REC IDS_FNSZFIRST+29
#define IDS_DEGREES IDS_FNSZFIRST+30
#define IDS_NEGATE IDS_FNSZFIRST+31
#define IDS_RSH IDS_FNSZFIRST+32
#define IDS_FNSZLAST IDS_RSH
#define IDS_ERRORS_FIRST IDS_FNSZLAST+1
// This is the list of error strings corresponding to SCERR_DIVIDEZERO..
#define IDS_DIVBYZERO IDS_ERRORS_FIRST
#define IDS_DOMAIN IDS_ERRORS_FIRST+1
#define IDS_UNDEFINED IDS_ERRORS_FIRST+2
#define IDS_POS_INFINITY IDS_ERRORS_FIRST+3
#define IDS_NEG_INFINITY IDS_ERRORS_FIRST+4
#define IDS_NOMEM IDS_ERRORS_FIRST+6
#define IDS_TOOMANY IDS_ERRORS_FIRST+7
#define IDS_OVERFLOW IDS_ERRORS_FIRST+8
#define IDS_NORESULT IDS_ERRORS_FIRST+9
#define IDS_INSUFFICIENT_DATA IDS_ERRORS_FIRST+10
#define CSTRINGSENGMAX IDS_INSUFFICIENT_DATA+1
// Arithmetic expression evaluator error strings
#define IDS_ERR_UNK_CH CSTRINGSENGMAX+1
#define IDS_ERR_UNK_FN CSTRINGSENGMAX+2
#define IDS_ERR_UNEX_NUM CSTRINGSENGMAX+3
#define IDS_ERR_UNEX_CH CSTRINGSENGMAX+4
#define IDS_ERR_UNEX_SZ CSTRINGSENGMAX+5
#define IDS_ERR_MISMATCH_CLOSE CSTRINGSENGMAX+6
#define IDS_ERR_UNEX_END CSTRINGSENGMAX+7
#define IDS_ERR_SG_INV_ERROR CSTRINGSENGMAX+8
#define IDS_ERR_INPUT_OVERFLOW CSTRINGSENGMAX+9
#define IDS_ERR_OUTPUT_OVERFLOW CSTRINGSENGMAX+10
#define SIDS_PLUS_MINUS L"0"
#define SIDS_CLEAR L"1"
#define SIDS_CE L"2"
#define SIDS_BACKSPACE L"3"
#define SIDS_DECIMAL_SEPARATOR L"4"
#define SIDS_EMPTY_STRING L"5"
#define SIDS_AND L"6"
#define SIDS_OR L"7"
#define SIDS_XOR L"8"
#define SIDS_LSH L"9"
#define SIDS_RSH L"10"
#define SIDS_DIVIDE L"11"
#define SIDS_MULTIPLY L"12"
#define SIDS_PLUS L"13"
#define SIDS_MINUS L"14"
#define SIDS_MOD L"15"
#define SIDS_YROOT L"16"
#define SIDS_POW_HAT L"17"
#define SIDS_INT L"18"
#define SIDS_ROL L"19"
#define SIDS_ROR L"20"
#define SIDS_NOT L"21"
#define SIDS_SIN L"22"
#define SIDS_COS L"23"
#define SIDS_TAN L"24"
#define SIDS_SINH L"25"
#define SIDS_COSH L"26"
#define SIDS_TANH L"27"
#define SIDS_LN L"28"
#define SIDS_LOG L"29"
#define SIDS_SQRT L"30"
#define SIDS_XPOW2 L"31"
#define SIDS_XPOW3 L"32"
#define SIDS_NFACTORIAL L"33"
#define SIDS_RECIPROCAL L"34"
#define SIDS_DMS L"35"
#define SIDS_CUBEROOT L"36"
#define SIDS_POWTEN L"37"
#define SIDS_PERCENT L"38"
#define SIDS_SCIENTIFIC_NOTATION L"39"
#define SIDS_PI L"40"
#define SIDS_EQUAL L"41"
#define SIDS_MC L"42"
#define SIDS_MR L"43"
#define SIDS_MS L"44"
#define SIDS_MPLUS L"45"
#define SIDS_MMINUS L"46"
#define SIDS_EXP L"47"
#define SIDS_OPEN_PAREN L"48"
#define SIDS_CLOSE_PAREN L"49"
#define SIDS_0 L"50"
#define SIDS_1 L"51"
#define SIDS_2 L"52"
#define SIDS_3 L"53"
#define SIDS_4 L"54"
#define SIDS_5 L"55"
#define SIDS_6 L"56"
#define SIDS_7 L"57"
#define SIDS_8 L"58"
#define SIDS_9 L"59"
#define SIDS_A L"60"
#define SIDS_B L"61"
#define SIDS_C L"62"
#define SIDS_D L"63"
#define SIDS_E L"64"
#define SIDS_F L"65"
#define SIDS_FRAC L"66"
#define SIDS_SIND L"67"
#define SIDS_COSD L"68"
#define SIDS_TAND L"69"
#define SIDS_ASIND L"70"
#define SIDS_ACOSD L"71"
#define SIDS_ATAND L"72"
#define SIDS_SINR L"73"
#define SIDS_COSR L"74"
#define SIDS_TANR L"75"
#define SIDS_ASINR L"76"
#define SIDS_ACOSR L"77"
#define SIDS_ATANR L"78"
#define SIDS_SING L"79"
#define SIDS_COSG L"80"
#define SIDS_TANG L"81"
#define SIDS_ASING L"82"
#define SIDS_ACOSG L"83"
#define SIDS_ATANG L"84"
#define SIDS_ASINH L"85"
#define SIDS_ACOSH L"86"
#define SIDS_ATANH L"87"
#define SIDS_POWE L"88"
#define SIDS_POWTEN2 L"89"
#define SIDS_SQRT2 L"90"
#define SIDS_SQR L"91"
#define SIDS_CUBE L"92"
#define SIDS_CUBERT L"93"
#define SIDS_FACT L"94"
#define SIDS_RECIPROC L"95"
#define SIDS_DEGREES L"96"
#define SIDS_NEGATE L"97"
#define SIDS_RSH2 L"98"
#define SIDS_DIVIDEBYZERO L"99"
#define SIDS_DOMAIN L"100"
#define SIDS_UNDEFINED L"101"
#define SIDS_POS_INFINITY L"102"
#define SIDS_NEG_INFINITY L"103"
#define SIDS_ABORTED L"104"
#define SIDS_NOMEM L"105"
#define SIDS_TOOMANY L"106"
#define SIDS_OVERFLOW L"107"
#define SIDS_NORESULT L"108"
#define SIDS_INSUFFICIENT_DATA L"109"
// 110 is skipped by CSTRINGSENGMAX
#define SIDS_ERR_UNK_CH L"111"
#define SIDS_ERR_UNK_FN L"112"
#define SIDS_ERR_UNEX_NUM L"113"
#define SIDS_ERR_UNEX_CH L"114"
#define SIDS_ERR_UNEX_SZ L"115"
#define SIDS_ERR_MISMATCH_CLOSE L"116"
#define SIDS_ERR_UNEX_END L"117"
#define SIDS_ERR_SG_INV_ERROR L"118"
#define SIDS_ERR_INPUT_OVERFLOW L"119"
#define SIDS_ERR_OUTPUT_OVERFLOW L"120"
__declspec(selectany) std::wstring g_sids[] =
{
std::wstring(SIDS_PLUS_MINUS),
std::wstring(SIDS_C),
std::wstring(SIDS_CE),
std::wstring(SIDS_BACKSPACE),
std::wstring(SIDS_DECIMAL_SEPARATOR),
std::wstring(SIDS_EMPTY_STRING),
std::wstring(SIDS_AND),
std::wstring(SIDS_OR),
std::wstring(SIDS_XOR),
std::wstring(SIDS_LSH),
std::wstring(SIDS_RSH),
std::wstring(SIDS_DIVIDE),
std::wstring(SIDS_MULTIPLY),
std::wstring(SIDS_PLUS),
std::wstring(SIDS_MINUS),
std::wstring(SIDS_MOD),
std::wstring(SIDS_YROOT),
std::wstring(SIDS_POW_HAT),
std::wstring(SIDS_INT),
std::wstring(SIDS_ROL),
std::wstring(SIDS_ROR),
std::wstring(SIDS_NOT),
std::wstring(SIDS_SIN),
std::wstring(SIDS_COS),
std::wstring(SIDS_TAN),
std::wstring(SIDS_SINH),
std::wstring(SIDS_COSH),
std::wstring(SIDS_TANH),
std::wstring(SIDS_LN),
std::wstring(SIDS_LOG),
std::wstring(SIDS_SQRT),
std::wstring(SIDS_XPOW2),
std::wstring(SIDS_XPOW3),
std::wstring(SIDS_NFACTORIAL),
std::wstring(SIDS_RECIPROCAL),
std::wstring(SIDS_DMS),
std::wstring(SIDS_CUBEROOT),
std::wstring(SIDS_POWTEN),
std::wstring(SIDS_PERCENT),
std::wstring(SIDS_SCIENTIFIC_NOTATION),
std::wstring(SIDS_PI),
std::wstring(SIDS_EQUAL),
std::wstring(SIDS_MC),
std::wstring(SIDS_MR),
std::wstring(SIDS_MS),
std::wstring(SIDS_MPLUS),
std::wstring(SIDS_MMINUS),
std::wstring(SIDS_EXP),
std::wstring(SIDS_OPEN_PAREN),
std::wstring(SIDS_CLOSE_PAREN),
std::wstring(SIDS_0),
std::wstring(SIDS_1),
std::wstring(SIDS_2),
std::wstring(SIDS_3),
std::wstring(SIDS_4),
std::wstring(SIDS_5),
std::wstring(SIDS_6),
std::wstring(SIDS_7),
std::wstring(SIDS_8),
std::wstring(SIDS_9),
std::wstring(SIDS_A),
std::wstring(SIDS_B),
std::wstring(SIDS_C),
std::wstring(SIDS_D),
std::wstring(SIDS_E),
std::wstring(SIDS_F),
std::wstring(SIDS_FRAC),
std::wstring(SIDS_SIND),
std::wstring(SIDS_COSD),
std::wstring(SIDS_TAND),
std::wstring(SIDS_ASIND),
std::wstring(SIDS_ACOSD),
std::wstring(SIDS_ATAND),
std::wstring(SIDS_SINR),
std::wstring(SIDS_COSR),
std::wstring(SIDS_TANR),
std::wstring(SIDS_ASINR),
std::wstring(SIDS_ACOSR),
std::wstring(SIDS_ATANR),
std::wstring(SIDS_SING),
std::wstring(SIDS_COSG),
std::wstring(SIDS_TANG),
std::wstring(SIDS_ASING),
std::wstring(SIDS_ACOSG),
std::wstring(SIDS_ATANG),
std::wstring(SIDS_ASINH),
std::wstring(SIDS_ACOSH),
std::wstring(SIDS_ATANH),
std::wstring(SIDS_POWE),
std::wstring(SIDS_POWTEN2),
std::wstring(SIDS_SQRT2),
std::wstring(SIDS_SQR),
std::wstring(SIDS_CUBE),
std::wstring(SIDS_CUBERT),
std::wstring(SIDS_FACT),
std::wstring(SIDS_RECIPROC),
std::wstring(SIDS_DEGREES),
std::wstring(SIDS_NEGATE),
std::wstring(SIDS_RSH),
std::wstring(SIDS_DIVIDEBYZERO),
std::wstring(SIDS_DOMAIN),
std::wstring(SIDS_UNDEFINED),
std::wstring(SIDS_POS_INFINITY),
std::wstring(SIDS_NEG_INFINITY),
std::wstring(SIDS_ABORTED),
std::wstring(SIDS_NOMEM),
std::wstring(SIDS_TOOMANY),
std::wstring(SIDS_OVERFLOW),
std::wstring(SIDS_NORESULT),
std::wstring(SIDS_INSUFFICIENT_DATA),
std::wstring(SIDS_ERR_UNK_CH),
std::wstring(SIDS_ERR_UNK_FN),
std::wstring(SIDS_ERR_UNEX_NUM),
std::wstring(SIDS_ERR_UNEX_CH),
std::wstring(SIDS_ERR_UNEX_SZ),
std::wstring(SIDS_ERR_MISMATCH_CLOSE),
std::wstring(SIDS_ERR_UNEX_END),
std::wstring(SIDS_ERR_SG_INV_ERROR),
std::wstring(SIDS_ERR_INPUT_OVERFLOW),
std::wstring(SIDS_ERR_OUTPUT_OVERFLOW)
};

View File

@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include "ICalcDisplay.h"
#include "IHistoryDisplay.h"
// maximum depth you can get by precedence. It is just an array's size limit.
static constexpr size_t MAXPRECDEPTH = 25;
// Helper class really a internal class to CCalcEngine, to accumulate each history line of text by collecting the
// operands, operator, unary operator etc. Since it is a seperate entity, it can be unit tested on its own but does
// rely on CCalcEngine calling it in appropriate order.
class CHistoryCollector {
public:
CHistoryCollector(ICalcDisplay *pCalcDisplay, std::shared_ptr<IHistoryDisplay> pHistoryDisplay, wchar_t decimalSymbol); // Can throw errors
~CHistoryCollector();
void AddOpndToHistory(std::wstring_view numStr, PRAT hNoNum, bool fRepetition = false);
void RemoveLastOpndFromHistory();
void AddBinOpToHistory(int nOpCode, bool fNoRepetition = true);
void ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher);
void AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE angletype);
void AddOpenBraceToHistory();
void AddCloseBraceToHistory();
void PushLastOpndStart(int ichOpndStart = -1);
void PopLastOpndStart();
void EnclosePrecInvertionBrackets();
bool FOpndAddedToHistory();
void CompleteHistoryLine(std::wstring_view numStr);
void ClearHistoryLine(std::wstring_view errStr);
int AddCommand(_In_ const std::shared_ptr<IExpressionCommand> & spCommand);
void UpdateHistoryExpression(uint32_t radix, int32_t precision);
void SetDecimalSymbol(wchar_t decimalSymbol);
private:
std::shared_ptr<IHistoryDisplay> m_pHistoryDisplay;
ICalcDisplay *m_pCalcDisplay;
int m_iCurLineHistStart; // index of the begginning of the current equation
// a sort of state, set to the index before 2 after 2 in the expression 2 + 3 say. Useful for auto correct portion of history and for
// attaching the unary op around the last operand
int m_lastOpStartIndex; // index of the beginning of the last operand added to the history
int m_lastBinOpStartIndex; // index of the beginning of the last binary operator added to the history
std::array<int, MAXPRECDEPTH> m_operandIndices; // Stack of index of opnd's beginning for each '('. A parallel array to m_hnoParNum, but abstracted independently of that
int m_curOperandIndex; // Stack index for the above stack
bool m_bLastOpndBrace; // iff the last opnd in history is already braced so we can avoid putting another one for unary operator
wchar_t m_decimalSymbol;
std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> m_spTokens;
std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> m_spCommands;
private:
void ReinitHistory();
int IchAddSzToEquationSz(std::wstring_view str, int icommandIndex);
void TruncateEquationSzFromIch(int ich);
void SetExpressionDisplay();
void InsertSzInEquationSz(std::wstring_view str, int icommandIndex, int ich);
std::shared_ptr<CalculatorVector<int>> GetOperandCommandsFromString(std::wstring_view numStr);
};

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
// Callback interface to be implemented by the clients of CCalcEngine
class ICalcDisplay {
public:
virtual void SetPrimaryDisplay(const std::wstring& pszText, bool isError) = 0;
virtual void SetIsInError(bool isInError) = 0;
virtual void SetExpressionDisplay(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens, _Inout_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &commands) = 0;
virtual void SetParenDisplayText(const std::wstring& pszText) = 0;
virtual void MaxDigitsReached() = 0; // not an error but still need to inform UI layer.
virtual void BinaryOperatorReceived() = 0;
virtual void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) = 0;
virtual void SetMemorizedNumbers(const std::vector<std::wstring>& memorizedNumbers) = 0;
virtual void MemoryItemChanged(unsigned int indexOfMemory) = 0;
};

View File

@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
// Callback interface to be implemented by the clients of CCalcEngine if they require equation history
class IHistoryDisplay {
public:
virtual ~IHistoryDisplay() {};
virtual unsigned int AddToHistory(_In_ std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> const &tokens, _In_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &commands, _In_ std::wstring_view result) = 0;
};

View File

@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
#pragma once
#include "RatPack/ratpak.h"
namespace CalcEngine
{
class Number
{
public:
Number() noexcept;
Number(int32_t sign, int32_t exp, std::vector<uint32_t> const& mantissa) noexcept;
explicit Number(PNUMBER p) noexcept;
PNUMBER ToPNUMBER() const;
int32_t const& Sign() const;
int32_t const& Exp() const;
std::vector<uint32_t> const& Mantissa() const;
bool IsZero() const;
private:
int32_t m_sign;
int32_t m_exp;
std::vector<uint32_t> m_mantissa;
};
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
#pragma once
#include "Number.h"
namespace CalcEngine
{
class Rational
{
public:
Rational() noexcept;
Rational(Number const& n) noexcept;
Rational(Number const& p, Number const& q) noexcept;
explicit Rational(PRAT prat) noexcept;
PRAT ToPRAT() const;
Number const& P() const;
Number const& Q() const;
bool IsZero() const;
private:
Number m_p;
Number m_q;
};
}

View File

@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**************************************************************************\
* *
* *
* *
* # # ##### *
* # # # # # *
* # # # # # # # *
* # ### ### # # *
* # # ### # # # ### # # ### ##### # ### ### ### *
* # ## # # # ## # # # # # # ## # # # *
* # # # # # # # # # ##### # # ##### # *
* # # # # # # # # # # # # # # ## *
* # # # # # # # # # ### # # ### ### ## *
* *
* *
* Infinte Precision Production Version *
* *
\**************************************************************************/
//
// RETAIL version of NUMOBJ math that uses Infinite Precision
//
#include "Ratpack/ratpak.h"
//
// Unary functions
//
void NumObjInvert(PRAT *phno, int32_t precision);
void NumObjNegate(PRAT *phno);
void NumObjAbs(PRAT *phno);
void NumObjSin(PRAT *phno, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
void NumObjCos(PRAT *phno, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
void NumObjTan(PRAT *phno, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
void NumObjAntiLog10(PRAT *phno, uint32_t radix, int32_t precision);
void NumObjNot(PRAT *phno, bool fIntegerMode, PRAT chopNum, uint32_t radix, int32_t precision);
//
// Comparison functions
//
bool NumObjIsZero(PRAT hno);
bool NumObjIsLess(PRAT hno1, PRAT hno2, int32_t precision);
bool NumObjIsLessEq(PRAT hno1, PRAT hno2, int32_t precision);
bool NumObjIsGreaterEq(PRAT hno1, PRAT hno2, int32_t precision);
bool NumObjIsEq(PRAT hno1, PRAT hno2, int32_t precision);
//
// Assignment operator. ('=' in C language)
//
void NumObjAssign(PRAT *phnol, PRAT hnor);
//
// Data type conversion functions
//
void NumObjSetIntValue(PRAT *phnol, LONG i );
void NumObjSetUlonglongValue(PRAT *phnol, ULONGLONG ul, uint32_t radix, int32_t precision);
ULONGLONG NumObjGetUlValue( PRAT hnol, uint32_t radix, int32_t precision);
// Returns a string representation of hnoNum
std::wstring NumObjToString(PRAT hnoNum, uint32_t radix, NUMOBJ_FMT fmt, int32_t precision);
//
// NumObjGetExp
//
// returns an int that equals the exponent of the NumObj
//
int32_t NumObjGetExp(PRAT hno);
//
// NumObjDestroy( hno )
//
// call this when you nolonger need the NumObj. Failure to do so
// will result in memory leaks.
//
void NumObjDestroy(PRAT *phno);

View File

@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// CalcErr.h
//
// Defines the error codes thrown by ratpak and caught by Calculator
//
//
// Ratpak errors are 32 bit values layed out as follows:
//
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-+-------+---------------------+-------------------------------+
// |S| R | Facility | Code |
// +-+-------+---------------------+-------------------------------+
//
// where
//
// S - Severity - indicates success/fail
//
// 0 - Success
// 1 - Fail
//
// R - Reserved - not currently used for anything
//
// r - reserved portion of the facility code. Reserved for internal
// use. Used to indicate HRESULT values that are not status
// values, but are instead message ids for display strings.
//
// Facility - is the facility code
//
// Code - is the actual error code
//
// This format is based losely on an OLE HRESULT and is compatible with the
// SUCCEEDED and FAILED marcos as well as the HRESULT_CODE macro
// CALC_E_DIVIDEBYZERO
//
// The current operation would require a divide by zero to complete
#define CALC_E_DIVIDEBYZERO ((DWORD)0x80000000)
// CALC_E_DOMAIN
//
// The given input is not within the domain of this function
#define CALC_E_DOMAIN ((DWORD)0x80000001)
// CALC_E_INDEFINITE
//
// The result of this function is undefined
#define CALC_E_INDEFINITE ((DWORD)0x80000002)
// CALC_E_POSINFINITY
//
// The result of this function is Positive Infinity.
#define CALC_E_POSINFINITY ((DWORD)0x80000003)
// CALC_E_NEGINFINITY
//
// The result of this function is Negative Infinity
#define CALC_E_NEGINFINITY ((DWORD)0x80000004)
// CALC_E_INVALIDRANGE
//
// The given input is within the domain of the function but is beyond
// the range for which calc can successfully compute the answer
#define CALC_E_INVALIDRANGE ((DWORD)0x80000006)
// CALC_E_OUTOFMEMORY
//
// There is not enough free memory to complete the requested function
#define CALC_E_OUTOFMEMORY ((DWORD)0x80000007)
// CALC_E_OVERFLOW
//
// The result of this operation is an overflow
#define CALC_E_OVERFLOW ((DWORD)0x80000008)
// CALC_E_NORESULT
//
// The result of this operation is undefined
#define CALC_E_NORESULT ((DWORD)0x80000009)

View File

@@ -0,0 +1,367 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File basex.c
// Copyright (C) 1995-97 Microsoft
// Date 03-14-97
//
//
// Description
//
// Contains number routines for internal base computations, these assume
// internal base is a power of 2.
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
void _mulnumx( PNUMBER *pa, PNUMBER b );
//----------------------------------------------------------------------------
//
// FUNCTION: mulnumx
//
// ARGUMENTS: pointer to a number and a second number, the
// base is always BASEX.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa *= b.
// This is a stub which prevents multiplication by 1, this is a big speed
// improvement.
//
//----------------------------------------------------------------------------
void __inline mulnumx( PNUMBER *pa, PNUMBER b )
{
if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
{
// If b is not one we multiply
if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 )
{
// pa and b are both nonone.
_mulnumx( pa, b );
}
else
{
// if pa is one and b isn't just copy b. and adjust the sign.
long sign = (*pa)->sign;
DUPNUM(*pa,b);
(*pa)->sign *= sign;
}
}
else
{
// B is +/- 1, But we do have to set the sign.
(*pa)->sign *= b->sign;
}
}
//----------------------------------------------------------------------------
//
// FUNCTION: _mulnumx
//
// ARGUMENTS: pointer to a number and a second number, the
// base is always BASEX.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa *= b.
// Assumes the base is BASEX of both numbers. This algorithm is the
// same one you learned in gradeschool, except the base isn't 10 it's
// BASEX.
//
//----------------------------------------------------------------------------
void _mulnumx( PNUMBER *pa, PNUMBER b )
{
PNUMBER c= nullptr; // c will contain the result.
PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa
MANTTYPE *ptra; // ptra is a pointer to the mantissa of a.
MANTTYPE *ptrb; // ptrb is a pointer to the mantissa of b.
MANTTYPE *ptrc; // ptrc is a pointer to the mantissa of c.
MANTTYPE *ptrcoffset; // ptrcoffset, is the anchor location of the next
// single digit multiply partial result.
long iadigit=0; // Index of digit being used in the first number.
long ibdigit=0; // Index of digit being used in the second number.
MANTTYPE da=0; // da is the digit from the fist number.
TWO_MANTTYPE cy=0; // cy is the carry resulting from the addition of
// a multiplied row into the result.
TWO_MANTTYPE mcy=0; // mcy is the resultant from a single
// multiply, AND the carry of that multiply.
long icdigit=0; // Index of digit being calculated in final result.
a=*pa;
ibdigit = a->cdigit + b->cdigit - 1;
createnum( c, ibdigit + 1 );
c->cdigit = ibdigit;
c->sign = a->sign * b->sign;
c->exp = a->exp + b->exp;
ptra = a->mant;
ptrcoffset = c->mant;
for ( iadigit = a->cdigit; iadigit > 0; iadigit-- )
{
da = *ptra++;
ptrb = b->mant;
// Shift ptrc, and ptrcoffset, one for each digit
ptrc = ptrcoffset++;
for ( ibdigit = b->cdigit; ibdigit > 0; ibdigit-- )
{
cy = 0;
mcy = (DWORDLONG)da * (*ptrb);
if ( mcy )
{
icdigit = 0;
if ( ibdigit == 1 && iadigit == 1 )
{
c->cdigit++;
}
}
// If result is nonzero, or while result of carry is nonzero...
while ( mcy || cy )
{
// update carry from addition(s) and multiply.
cy += (TWO_MANTTYPE)ptrc[icdigit]+((DWORD)mcy&((DWORD)~BASEX));
// update result digit from
ptrc[icdigit++]=(MANTTYPE)((DWORD)cy&((DWORD)~BASEX));
// update carries from
mcy >>= BASEXPWR;
cy >>= BASEXPWR;
}
ptrb++;
ptrc++;
}
}
// prevent different kinds of zeros, by stripping leading duplicate zeroes.
// digits are in order of increasing significance.
while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 )
{
c->cdigit--;
}
destroynum( *pa );
*pa=c;
}
//-----------------------------------------------------------------------------
//
// FUNCTION: numpowlongx
//
// ARGUMENTS: root as number power as long
// number.
//
// RETURN: None root is changed.
//
// DESCRIPTION: changes numeric representation of root to
// root ** power. Assumes base BASEX
// decomposes the exponent into it's sums of powers of 2, so on average
// it will take n+n/2 multiplies where n is the highest on bit.
//
//-----------------------------------------------------------------------------
void numpowlongx( _Inout_ PNUMBER *proot, _In_ long power )
{
PNUMBER lret = longtonum( 1, BASEX );
// Once the power remaining is zero we are done.
while ( power > 0 )
{
// If this bit in the power decomposition is on, multiply the result
// by the root number.
if ( power & 1 )
{
mulnumx( &lret, *proot );
}
// multiply the root number by itself to scale for the next bit (i.e.
// square it.
mulnumx( proot, *proot );
// move the next bit of the power into place.
power >>= 1;
}
destroynum( *proot );
*proot=lret;
}
void _divnumx( PNUMBER *pa, PNUMBER b, int32_t precision);
//----------------------------------------------------------------------------
//
// FUNCTION: divnumx
//
// ARGUMENTS: pointer to a number, a second number and precision.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa /= b.
// Assumes radix is the internal radix representation.
// This is a stub which prevents division by 1, this is a big speed
// improvement.
//
//----------------------------------------------------------------------------
void __inline divnumx( PNUMBER *pa, PNUMBER b, int32_t precision)
{
if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
{
// b is not one.
if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 )
{
// pa and b are both not one.
_divnumx( pa, b, precision);
}
else
{
// if pa is one and b is not one, just copy b, and adjust the sign.
long sign = (*pa)->sign;
DUPNUM(*pa,b);
(*pa)->sign *= sign;
}
}
else
{
// b is one so don't divide, but set the sign.
(*pa)->sign *= b->sign;
}
}
//----------------------------------------------------------------------------
//
// FUNCTION: _divnumx
//
// ARGUMENTS: pointer to a number, a second number and precision.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa /= b.
// Assumes radix is the internal radix representation.
//
//----------------------------------------------------------------------------
void _divnumx( PNUMBER *pa, PNUMBER b, int32_t precision)
{
PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa
PNUMBER c= nullptr; // c will contain the result.
PNUMBER lasttmp = nullptr; // lasttmp allows a backup when the algorithm
// guesses one bit too far.
PNUMBER tmp = nullptr; // current guess being worked on for divide.
PNUMBER rem = nullptr; // remainder after applying guess.
long cdigits; // count of digits for answer.
MANTTYPE *ptrc; // ptrc is a pointer to the mantissa of c.
long thismax = precision + g_ratio; // set a maximum number of internal digits
// to shoot for in the divide.
a=*pa;
if ( thismax < a->cdigit )
{
// a has more digits than precision specified, bump up digits to shoot
// for.
thismax = a->cdigit;
}
if ( thismax < b->cdigit )
{
// b has more digits than precision specified, bump up digits to shoot
// for.
thismax = b->cdigit;
}
// Create c (the divide answer) and set up exponent and sign.
createnum( c, thismax + 1 );
c->exp = (a->cdigit+a->exp) - (b->cdigit+b->exp) + 1;
c->sign = a->sign * b->sign;
ptrc = c->mant + thismax;
cdigits = 0;
DUPNUM( rem, a );
rem->sign = b->sign;
rem->exp = b->cdigit + b->exp - rem->cdigit;
while ( cdigits++ < thismax && !zernum(rem) )
{
long digit = 0;
*ptrc = 0;
while ( !lessnum( rem, b ) )
{
digit = 1;
DUPNUM( tmp, b );
destroynum( lasttmp );
lasttmp=longtonum( 0, BASEX );
while ( lessnum( tmp, rem ) )
{
destroynum( lasttmp );
DUPNUM(lasttmp,tmp);
addnum( &tmp, tmp, BASEX );
digit *= 2;
}
if ( lessnum( rem, tmp ) )
{
// too far, back up...
destroynum( tmp );
digit /= 2;
tmp=lasttmp;
lasttmp= nullptr;
}
tmp->sign *= -1;
addnum( &rem, tmp, BASEX );
destroynum( tmp );
destroynum( lasttmp );
*ptrc |= digit;
}
rem->exp++;
ptrc--;
}
cdigits--;
if ( c->mant != ++ptrc )
{
memmove( c->mant, ptrc, (int)(cdigits*sizeof(MANTTYPE)) );
}
if ( !cdigits )
{
// A zero, make sure no wierd exponents creep in
c->exp = 0;
c->cdigit = 1;
}
else
{
c->cdigit = cdigits;
c->exp -= cdigits;
// prevent different kinds of zeros, by stripping leading duplicate
// zeroes. digits are in order of increasing significance.
while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 )
{
c->cdigit--;
}
}
destroynum( rem );
destroynum( *pa );
*pa=c;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,540 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File exp.c
// Copyright (C) 1995-96 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains exp, and log functions for rationals
//
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
//-----------------------------------------------------------------------------
//
// FUNCTION: exprat
//
// ARGUMENTS: x PRAT representation of number to exponentiate
//
// RETURN: exp of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___
// \ ] X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j j+1
// /__]
// j=0
//
// thisterm = X ; and stop when thisterm < precision used.
// 0 n
//
//-----------------------------------------------------------------------------
void _exprat( PRAT *px, int32_t precision)
{
CREATETAYLOR();
addnum(&(pret->pp),num_one, BASEX);
addnum(&(pret->pq),num_one, BASEX);
DUPRAT(thisterm,pret);
n2=longtonum(0L, BASEX);
do {
NEXTTERM(*px, INC(n2) DIVNUM(n2), precision);
} while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
void exprat( PRAT *px, uint32_t radix, int32_t precision)
{
PRAT pwr= nullptr;
PRAT pint= nullptr;
long intpwr;
if ( rat_gt( *px, rat_max_exp, precision) || rat_lt( *px, rat_min_exp, precision) )
{
// Don't attempt exp of anything large.
throw( CALC_E_DOMAIN );
}
DUPRAT(pwr,rat_exp);
DUPRAT(pint,*px);
intrat(&pint, radix, precision);
intpwr = rattolong(pint, radix, precision);
ratpowlong( &pwr, intpwr, precision);
subrat(px, pint, precision);
// It just so happens to be an integral power of e.
if ( rat_gt( *px, rat_negsmallest, precision) && rat_lt( *px, rat_smallest, precision) )
{
DUPRAT(*px,pwr);
}
else
{
_exprat(px, precision);
mulrat(px, pwr, precision);
}
destroyrat( pwr );
destroyrat( pint );
}
//-----------------------------------------------------------------------------
//
// FUNCTION: lograt, _lograt
//
// ARGUMENTS: x PRAT representation of number to logarithim
//
// RETURN: log of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___
// \ ] j*(1-X)
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j j+1
// /__]
// j=0
//
// thisterm = X ; and stop when thisterm < precision used.
// 0 n
//
// Number is scaled between one and e_to_one_half prior to taking the
// log. This is to keep execution time from exploding.
//
//
//-----------------------------------------------------------------------------
void _lograt( PRAT *px, int32_t precision)
{
CREATETAYLOR();
createrat(thisterm);
// sub one from x
(*px)->pq->sign *= -1;
addnum(&((*px)->pp),(*px)->pq, BASEX);
(*px)->pq->sign *= -1;
DUPRAT(pret,*px);
DUPRAT(thisterm,*px);
n2=longtonum(1L, BASEX);
(*px)->pp->sign *= -1;
do {
NEXTTERM(*px, MULNUM(n2) INC(n2) DIVNUM(n2), precision);
TRIMTOP(*px, precision);
} while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
void lograt( PRAT *px, int32_t precision)
{
bool fneglog;
PRAT pwr = nullptr; // pwr is the large scaling factor.
PRAT offset = nullptr; // offset is the incremental scaling factor.
// Check for someone taking the log of zero or a negative number.
if ( rat_le( *px, rat_zero, precision) )
{
throw( CALC_E_DOMAIN );
}
// Get number > 1, for scaling
fneglog = rat_lt( *px, rat_one, precision);
if ( fneglog )
{
// WARNING: This is equivalent to doing *px = 1 / *px
PNUMBER pnumtemp= nullptr;
pnumtemp = (*px)->pp;
(*px)->pp = (*px)->pq;
(*px)->pq = pnumtemp;
}
// Scale the number within BASEX factor of 1, for the large scale.
// log(x*2^(BASEXPWR*k)) = BASEXPWR*k*log(2)+log(x)
if ( LOGRAT2(*px) > 1 )
{
// Take advantage of px's base BASEX to scale quickly down to
// a reasonable range.
long intpwr;
intpwr=LOGRAT2(*px)-1;
(*px)->pq->exp += intpwr;
pwr=longtorat(intpwr*BASEXPWR);
mulrat(&pwr, ln_two, precision);
// ln(x+e)-ln(x) looks close to e when x is close to one using some
// expansions. This means we can trim past precision digits+1.
TRIMTOP(*px, precision);
}
else
{
DUPRAT(pwr,rat_zero);
}
DUPRAT(offset,rat_zero);
// Scale the number between 1 and e_to_one_half, for the small scale.
while ( rat_gt( *px, e_to_one_half, precision) )
{
divrat( px, e_to_one_half, precision);
addrat( &offset, rat_one, precision);
}
_lograt(px, precision);
// Add the large and small scaling factors, take into account
// small scaling was done in e_to_one_half chunks.
divrat(&offset, rat_two, precision);
addrat(&pwr, offset, precision);
// And add the resulting scaling factor to the answer.
addrat(px, pwr, precision);
trimit(px, precision);
// If number started out < 1 rescale answer to negative.
if ( fneglog )
{
(*px)->pp->sign *= -1;
}
destroyrat(offset);
destroyrat(pwr);
}
void log10rat( PRAT *px, int32_t precision)
{
lograt(px, precision);
divrat(px, ln_ten, precision);
}
//
// return if the given x is even number. The assumption here is its numberator is 1 and we are testing the numerator is
// even or not
bool IsEven(PRAT x, uint32_t radix, int32_t precision)
{
PRAT tmp = nullptr;
bool bRet = false;
DUPRAT(tmp, x);
divrat(&tmp, rat_two, precision);
fracrat(&tmp, radix, precision);
addrat(&tmp, tmp, precision);
subrat(&tmp, rat_one, precision);
if ( rat_lt( tmp, rat_zero, precision))
{
bRet = true;
}
destroyrat(tmp);
return bRet;
}
//---------------------------------------------------------------------------
//
// FUNCTION: powrat
//
// ARGUMENTS: PRAT *px, PRAT y, uint32_t radix, int32_t precision
//
// RETURN: none, sets *px to *px to the y.
//
// EXPLANATION: Calculates the power of both px and
// handles special cases where px is a perfect root.
// Assumes, all checking has been done on validity of numbers.
//
//
//---------------------------------------------------------------------------
void powrat(PRAT *px, PRAT y, uint32_t radix, int32_t precision)
{
// Handle cases where px or y is 0 by calling powratcomp directly
if (zerrat(*px) || zerrat(y))
{
powratcomp(px, y, radix, precision);
return;
}
// When y is 1, return px
if (rat_equ(y, rat_one, precision))
{
return;
}
try
{
powratNumeratorDenominator(px, y, radix, precision);
}
catch (...)
{
// If calculating the power using numerator/denominator
// failed, fallback to the less accurate method of
// passing in the original y
powratcomp(px, y, radix, precision);
}
}
void powratNumeratorDenominator(PRAT *px, PRAT y, uint32_t radix, int32_t precision)
{
// Prepare rationals
PRAT yNumerator = nullptr;
PRAT yDenominator = nullptr;
DUPRAT(yNumerator, rat_zero); // yNumerator->pq is 1 one
DUPRAT(yDenominator, rat_zero); // yDenominator->pq is 1 one
DUPNUM(yNumerator->pp, y->pp);
DUPNUM(yDenominator->pp, y->pq);
// Calculate the following use the Powers of Powers rule:
// px ^ (yNum/yDenom) == px ^ yNum ^ (1/yDenom)
// 1. For px ^ yNum, we call powratcomp directly which will call ratpowlong
// and store the result in pxPowNum
// 2. For pxPowNum ^ (1/yDenom), we call powratcomp
// 3. Validate the result of 2 by adding/subtracting 0.5, flooring and call powratcomp with yDenom
// on the floored result.
// 1. Initialize result.
PRAT pxPow = nullptr;
DUPRAT(pxPow, *px);
// 2. Calculate pxPow = px ^ yNumerator
// if yNumerator is not 1
if (!rat_equ(yNumerator, rat_one, precision))
{
powratcomp(&pxPow, yNumerator, radix, precision);
}
// 2. Calculate pxPowNumDenom = pxPowNum ^ (1/yDenominator),
// if yDenominator is not 1
if (!rat_equ(yDenominator, rat_one, precision))
{
// Calculate 1 over y
PRAT oneoveryDenom = nullptr;
DUPRAT(oneoveryDenom, rat_one);
divrat(&oneoveryDenom, yDenominator, precision);
// ##################################
// Take the oneoveryDenom power
// ##################################
PRAT originalResult = nullptr;
DUPRAT(originalResult, pxPow);
powratcomp(&originalResult, oneoveryDenom, radix, precision);
// ##################################
// Round the originalResult to roundedResult
// ##################################
PRAT roundedResult = nullptr;
DUPRAT(roundedResult, originalResult);
if (roundedResult->pp->sign == -1)
{
subrat(&roundedResult, rat_half, precision);
}
else
{
addrat(&roundedResult, rat_half, precision);
}
intrat(&roundedResult, radix, precision);
// ##################################
// Take the yDenom power of the roundedResult.
// ##################################
PRAT roundedPower = nullptr;
DUPRAT(roundedPower, roundedResult);
powratcomp(&roundedPower, yDenominator, radix, precision);
// ##################################
// if roundedPower == px,
// we found an exact power in roundedResult
// ##################################
if (rat_equ(roundedPower, pxPow, precision))
{
DUPRAT(*px, roundedResult);
}
else
{
DUPRAT(*px, originalResult);
}
destroyrat(oneoveryDenom);
destroyrat(originalResult);
destroyrat(roundedResult);
destroyrat(roundedPower);
}
else
{
DUPRAT(*px, pxPow);
}
destroyrat(yNumerator);
destroyrat(yDenominator);
destroyrat(pxPow);
}
//---------------------------------------------------------------------------
//
// FUNCTION: powratcomp
//
// ARGUMENTS: PRAT *px, and PRAT y
//
// RETURN: none, sets *px to *px to the y.
//
// EXPLANATION: This uses x^y=e(y*ln(x)), or a more exact calculation where
// y is an integer.
// Assumes, all checking has been done on validity of numbers.
//
//
//---------------------------------------------------------------------------
void powratcomp(PRAT *px, PRAT y, uint32_t radix, int32_t precision)
{
long sign = ((*px)->pp->sign * (*px)->pq->sign);
// Take the absolute value
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
if ( zerrat( *px ) )
{
// *px is zero.
if ( rat_lt( y, rat_zero, precision) )
{
throw( CALC_E_DOMAIN );
}
else if ( zerrat( y ) )
{
// *px and y are both zero, special case a 1 return.
DUPRAT(*px,rat_one);
// Ensure sign is positive.
sign = 1;
}
}
else
{
PRAT pxint= nullptr;
DUPRAT(pxint,*px);
subrat(&pxint, rat_one, precision);
if ( rat_gt( pxint, rat_negsmallest, precision) &&
rat_lt( pxint, rat_smallest, precision) && ( sign == 1 ) )
{
// *px is one, special case a 1 return.
DUPRAT(*px,rat_one);
// Ensure sign is positive.
sign = 1;
}
else
{
// Only do the exp if the number isn't zero or one
PRAT podd = nullptr;
DUPRAT(podd,y);
fracrat(&podd, radix, precision);
if ( rat_gt( podd, rat_negsmallest, precision) && rat_lt( podd, rat_smallest, precision) )
{
// If power is an integer let ratpowlong deal with it.
PRAT iy = nullptr;
long inty;
DUPRAT(iy,y);
subrat(&iy, podd, precision);
inty = rattolong(iy, radix, precision);
PRAT plnx = nullptr;
DUPRAT(plnx,*px);
lograt(&plnx, precision);
mulrat(&plnx, iy, precision);
if ( rat_gt( plnx, rat_max_exp, precision) || rat_lt( plnx, rat_min_exp, precision) )
{
// Don't attempt exp of anything large or small.A
destroyrat(plnx);
destroyrat(iy);
destroyrat(pxint);
destroyrat(podd);
throw( CALC_E_DOMAIN );
}
destroyrat(plnx);
ratpowlong(px, inty, precision);
if ( ( inty & 1 ) == 0 )
{
sign=1;
}
destroyrat(iy);
}
else
{
// power is a fraction
if ( sign == -1 )
{
// Need to throw an error if the exponent has an even denominator.
// As a first step, the numerator and denominator must be divided by 2 as many times as
// possible, so that 2/6 is allowed.
// If the final numerator is still even, the end result should be positive.
PRAT pNumerator = nullptr;
PRAT pDenominator = nullptr;
bool fBadExponent = false;
// Get the numbers in arbitrary precision rational number format
DUPRAT(pNumerator, rat_zero); // pNumerator->pq is 1 one
DUPRAT(pDenominator, rat_zero); // pDenominator->pq is 1 one
DUPNUM(pNumerator->pp, y->pp);
pNumerator->pp->sign = 1;
DUPNUM(pDenominator->pp, y->pq);
pDenominator->pp->sign = 1;
while (IsEven(pNumerator, radix, precision) && IsEven(pDenominator, radix, precision)) // both Numerator & denominator is even
{
divrat(&pNumerator, rat_two, precision);
divrat(&pDenominator, rat_two, precision);
}
if (IsEven(pDenominator, radix, precision)) // denominator is still even
{
fBadExponent = true;
}
if (IsEven(pNumerator, radix, precision)) // numerator is still even
{
sign = 1;
}
destroyrat(pNumerator);
destroyrat(pDenominator);
if (fBadExponent)
{
throw( CALC_E_DOMAIN );
}
}
else
{
// If the exponent is not odd disregard the sign.
sign = 1;
}
lograt(px, precision);
mulrat(px, y, precision);
exprat(px, radix, precision);
}
destroyrat(podd);
}
destroyrat(pxint);
}
(*px)->pp->sign *= sign;
}

View File

@@ -0,0 +1,258 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File fact.c
// Copyright (C) 1995-96 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains fact(orial) and supporting _gamma functions.
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
#define ABSRAT(x) (((x)->pp->sign=1),((x)->pq->sign=1))
#define NEGATE(x) ((x)->pp->sign *= -1)
//-----------------------------------------------------------------------------
//
// FUNCTION: factrat, _gamma, gamma
//
// ARGUMENTS: x PRAT representation of number to take the sine of
//
// RETURN: factorial of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2j
// n \ ] A 1 A
// A \ -----[ ---- - ---------------]
// / (2j)! n+2j (n+2j+1)(2j+1)
// /__]
// j=0
//
// / oo
// | n-1 -x __
// This was derived from | x e dx = |
// | | (n) { = (n-1)! for +integers}
// / 0
//
// It can be shown that the above series is within precision if A is chosen
// big enough.
// A n precision
// Based on the relation ne = A 10 A was chosen as
//
// precision
// A = ln(Base /n)+1
// A += n*ln(A) This is close enough for precision > base and n < 1.5
//
//
//-----------------------------------------------------------------------------
void _gamma( PRAT *pn, uint32_t radix, int32_t precision)
{
PRAT factorial= nullptr;
PNUMBER count= nullptr;
PRAT tmp= nullptr;
PRAT one_pt_five= nullptr;
PRAT a= nullptr;
PRAT a2= nullptr;
PRAT term= nullptr;
PRAT sum= nullptr;
PRAT err= nullptr;
PRAT mpy= nullptr;
PRAT ratprec = nullptr;
PRAT ratRadix = nullptr;
long oldprec;
// Set up constants and initial conditions
oldprec = precision;
ratprec = longtorat( oldprec );
// Find the best 'A' for convergence to the required precision.
a=longtorat( radix );
lograt(&a, precision);
mulrat(&a, ratprec, precision);
// Really is -ln(n)+1, but -ln(n) will be < 1
// if we scale n between 0.5 and 1.5
addrat(&a, rat_two, precision);
DUPRAT(tmp,a);
lograt(&tmp, precision);
mulrat(&tmp, *pn, precision);
addrat(&a, tmp, precision);
addrat(&a, rat_one, precision);
// Calculate the necessary bump in precision and up the precision.
// The following code is equivalent to
// precision += ln(exp(a)*pow(a,n+1.5))-ln(radix));
DUPRAT(tmp,*pn);
one_pt_five=longtorat( 3L );
divrat( &one_pt_five, rat_two, precision);
addrat( &tmp, one_pt_five, precision);
DUPRAT(term,a);
powratcomp( &term, tmp, radix, precision);
DUPRAT( tmp, a );
exprat( &tmp, radix, precision);
mulrat( &term, tmp, precision);
lograt( &term, precision);
ratRadix = longtorat(radix);
DUPRAT(tmp,ratRadix);
lograt( &tmp, precision);
subrat( &term, tmp, precision);
precision += rattolong( term, radix, precision);
// Set up initial terms for series, refer to series in above comment block.
DUPRAT(factorial,rat_one); // Start factorial out with one
count = longtonum( 0L, BASEX );
DUPRAT(mpy,a);
powratcomp(&mpy,*pn, radix, precision);
// a2=a^2
DUPRAT(a2,a);
mulrat(&a2, a, precision);
// sum=(1/n)-(a/(n+1))
DUPRAT(sum,rat_one);
divrat(&sum, *pn, precision);
DUPRAT(tmp,*pn);
addrat(&tmp, rat_one, precision);
DUPRAT(term,a);
divrat(&term, tmp, precision);
subrat(&sum, term, precision);
DUPRAT(err,ratRadix);
NEGATE(ratprec);
powratcomp(&err,ratprec, radix, precision);
divrat(&err, ratRadix, precision);
// Just get something not tiny in term
DUPRAT(term, rat_two );
// Loop until precision is reached, or asked to halt.
while ( !zerrat( term ) && rat_gt( term, err, precision) )
{
addrat(pn, rat_two, precision);
// WARNING: mixing numbers and rationals here.
// for speed and efficiency.
INC(count);
mulnumx(&(factorial->pp),count);
INC(count)
mulnumx(&(factorial->pp),count);
divrat(&factorial, a2, precision);
DUPRAT(tmp,*pn);
addrat( &tmp, rat_one, precision);
destroyrat(term);
createrat(term);
DUPNUM(term->pp,count);
DUPNUM(term->pq,num_one);
addrat( &term, rat_one, precision);
mulrat( &term, tmp, precision);
DUPRAT(tmp,a);
divrat( &tmp, term, precision);
DUPRAT(term,rat_one);
divrat( &term, *pn, precision);
subrat( &term, tmp, precision);
divrat (&term, factorial, precision);
addrat( &sum, term, precision);
ABSRAT(term);
}
// Multiply by factor.
mulrat( &sum, mpy, precision);
// And cleanup
precision = oldprec;
destroyrat(ratprec);
destroyrat(err);
destroyrat(term);
destroyrat(a);
destroyrat(a2);
destroyrat(tmp);
destroyrat(one_pt_five);
destroynum(count);
destroyrat(factorial);
destroyrat(*pn);
DUPRAT(*pn,sum);
destroyrat(sum);
}
void factrat( PRAT *px, uint32_t radix, int32_t precision)
{
PRAT fact = nullptr;
PRAT frac = nullptr;
PRAT neg_rat_one = nullptr;
if ( rat_gt( *px, rat_max_fact, precision) || rat_lt( *px, rat_min_fact, precision) )
{
// Don't attempt factorial of anything too large or small.
throw CALC_E_OVERFLOW;
}
DUPRAT(fact,rat_one);
DUPRAT(neg_rat_one,rat_one);
neg_rat_one->pp->sign *= -1;
DUPRAT( frac, *px );
fracrat( &frac, radix, precision);
// Check for negative integers and throw an error.
if ( ( zerrat(frac) || ( LOGRATRADIX(frac) <= -precision) ) &&
( (*px)->pp->sign * (*px)->pq->sign == -1 ) )
{
throw CALC_E_DOMAIN;
}
while ( rat_gt( *px, rat_zero, precision) &&
( LOGRATRADIX(*px) > -precision) )
{
mulrat( &fact, *px, precision);
subrat( px, rat_one, precision);
}
// Added to make numbers 'close enough' to integers use integer factorial.
if ( LOGRATRADIX(*px) <= -precision)
{
DUPRAT((*px),rat_zero);
intrat(&fact, radix, precision);
}
while ( rat_lt( *px, neg_rat_one, precision) )
{
addrat( px, rat_one, precision);
divrat( &fact, *px, precision);
}
if ( rat_neq( *px, rat_zero, precision) )
{
addrat( px, rat_one, precision);
_gamma( px, radix, precision);
mulrat( px, fact, precision);
}
else
{
DUPRAT(*px,fact);
}
destroyrat(fact);
destroyrat(frac);
destroyrat(neg_rat_one);
}

View File

@@ -0,0 +1,342 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File itrans.c
// Copyright (C) 1995-96 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains inverse sin, cos, tan functions for rationals
//
// Special Information
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
void ascalerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, int32_t precision)
{
switch ( angletype )
{
case ANGLE_RAD:
break;
case ANGLE_DEG:
divrat( pa, two_pi, precision);
mulrat( pa, rat_360, precision);
break;
case ANGLE_GRAD:
divrat( pa, two_pi, precision);
mulrat( pa, rat_400, precision);
break;
}
}
//-----------------------------------------------------------------------------
//
// FUNCTION: asinrat, _asinrat
//
// ARGUMENTS: x PRAT representation of number to take the inverse
// sine of
// RETURN: asin of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2 2
// \ ] (2j+1) X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j+2)*(2j+3)
// /__]
// j=0
//
// thisterm = X ; and stop when thisterm < precision used.
// 0 n
//
// If abs(x) > 0.85 then an alternate form is used
// pi/2-sgn(x)*asin(sqrt(1-x^2)
//
//
//-----------------------------------------------------------------------------
void _asinrat( PRAT *px, int32_t precision)
{
CREATETAYLOR();
DUPRAT(pret,*px);
DUPRAT(thisterm,*px);
DUPNUM(n2,num_one);
do
{
NEXTTERM(xx,MULNUM(n2) MULNUM(n2)
INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision);
}
while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
void asinanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
asinrat( pa, radix, precision);
ascalerat( pa, angletype, precision);
}
void asinrat( PRAT *px, uint32_t radix, int32_t precision)
{
long sgn;
PRAT pret= nullptr;
PRAT phack= nullptr;
sgn = (*px)->pp->sign* (*px)->pq->sign;
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
// Avoid the really bad part of the asin curve near +/-1.
DUPRAT(phack,*px);
subrat(&phack, rat_one, precision);
// Since *px might be epsilon near zero we must set it to zero.
if ( rat_le(phack, rat_smallest, precision) && rat_ge(phack, rat_negsmallest, precision) )
{
destroyrat(phack);
DUPRAT( *px, pi_over_two );
}
else
{
destroyrat(phack);
if ( rat_gt( *px, pt_eight_five, precision) )
{
if ( rat_gt( *px, rat_one, precision) )
{
subrat( px, rat_one, precision);
if ( rat_gt( *px, rat_smallest, precision) )
{
throw( CALC_E_DOMAIN );
}
else
{
DUPRAT(*px,rat_one);
}
}
DUPRAT(pret,*px);
mulrat( px, pret, precision);
(*px)->pp->sign *= -1;
addrat( px, rat_one, precision);
rootrat( px, rat_two, radix, precision);
_asinrat( px, precision);
(*px)->pp->sign *= -1;
addrat( px, pi_over_two, precision);
destroyrat(pret);
}
else
{
_asinrat( px, precision);
}
}
(*px)->pp->sign = sgn;
(*px)->pq->sign = 1;
}
//-----------------------------------------------------------------------------
//
// FUNCTION: acosrat, _acosrat
//
// ARGUMENTS: x PRAT representation of number to take the inverse
// cosine of
// RETURN: acos of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2 2
// \ ] (2j+1) X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j+2)*(2j+3)
// /__]
// j=0
//
// thisterm = 1 ; and stop when thisterm < precision used.
// 0 n
//
// In this case pi/2-asin(x) is used. At least for now _acosrat isn't
// called.
//
//-----------------------------------------------------------------------------
void acosanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
acosrat( pa, radix, precision);
ascalerat( pa, angletype, precision);
}
void _acosrat( PRAT *px, int32_t precision)
{
CREATETAYLOR();
createrat(thisterm);
thisterm->pp=longtonum( 1L, BASEX );
thisterm->pq=longtonum( 1L, BASEX );
DUPNUM(n2,num_one);
do
{
NEXTTERM(xx,MULNUM(n2) MULNUM(n2)
INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision);
}
while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
void acosrat( PRAT *px, uint32_t radix, int32_t precision)
{
long sgn;
sgn = (*px)->pp->sign*(*px)->pq->sign;
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
if ( rat_equ( *px, rat_one, precision) )
{
if ( sgn == -1 )
{
DUPRAT(*px,pi);
}
else
{
DUPRAT( *px, rat_zero );
}
}
else
{
(*px)->pp->sign = sgn;
asinrat( px, radix, precision);
(*px)->pp->sign *= -1;
addrat(px, pi_over_two, precision);
}
}
//-----------------------------------------------------------------------------
//
// FUNCTION: atanrat, _atanrat
//
// ARGUMENTS: x PRAT representation of number to take the inverse
// hyperbolic tangent of
//
// RETURN: atanh of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2
// \ ] (2j)*X (-1^j)
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j+2)
// /__]
// j=0
//
// thisterm = X ; and stop when thisterm < precision used.
// 0 n
//
// If abs(x) > 0.85 then an alternate form is used
// asin(x/sqrt(q+x^2))
//
// And if abs(x) > 2.0 then this form is used.
//
// pi/2 - atan(1/x)
//
//-----------------------------------------------------------------------------
void atananglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
atanrat( pa, radix, precision);
ascalerat( pa, angletype, precision);
}
void _atanrat( PRAT *px, int32_t precision)
{
CREATETAYLOR();
DUPRAT(pret,*px);
DUPRAT(thisterm,*px);
DUPNUM(n2,num_one);
xx->pp->sign *= -1;
do {
NEXTTERM(xx,MULNUM(n2) INC(n2) INC(n2) DIVNUM(n2), precision);
} while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
void atanrat( PRAT *px, uint32_t radix, int32_t precision)
{
long sgn;
PRAT tmpx= nullptr;
sgn = (*px)->pp->sign * (*px)->pq->sign;
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
if ( rat_gt( (*px), pt_eight_five, precision) )
{
if ( rat_gt( (*px), rat_two, precision) )
{
(*px)->pp->sign = sgn;
(*px)->pq->sign = 1;
DUPRAT(tmpx,rat_one);
divrat(&tmpx, (*px), precision);
_atanrat(&tmpx, precision);
tmpx->pp->sign = sgn;
tmpx->pq->sign = 1;
DUPRAT(*px,pi_over_two);
subrat(px, tmpx, precision);
destroyrat( tmpx );
}
else
{
(*px)->pp->sign = sgn;
DUPRAT(tmpx,*px);
mulrat( &tmpx, *px, precision);
addrat( &tmpx, rat_one, precision);
rootrat( &tmpx, rat_two, radix, precision);
divrat( px, tmpx, precision);
destroyrat( tmpx );
asinrat( px, radix, precision);
(*px)->pp->sign = sgn;
(*px)->pq->sign = 1;
}
}
else
{
(*px)->pp->sign = sgn;
(*px)->pq->sign = 1;
_atanrat( px, precision);
}
if ( rat_gt( *px, pi_over_two, precision) )
{
subrat( px, pi, precision);
}
}

View File

@@ -0,0 +1,160 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File itransh.c
// Copyright (C) 1995-97 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains inverse hyperbolic sin, cos, and tan functions.
//
// Special Information
//
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
//-----------------------------------------------------------------------------
//
// FUNCTION: asinhrat
//
// ARGUMENTS: x PRAT representation of number to take the inverse
// hyperbolic sine of
// RETURN: asinh of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2 2
// \ ] -(2j+1) X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j+2)*(2j+3)
// /__]
// j=0
//
// thisterm = X ; and stop when thisterm < precision used.
// 0 n
//
// For abs(x) < .85, and
//
// asinh(x) = log(x+sqrt(x^2+1))
//
// For abs(x) >= .85
//
//-----------------------------------------------------------------------------
void asinhrat( PRAT *px, uint32_t radix, int32_t precision)
{
PRAT neg_pt_eight_five = nullptr;
DUPRAT(neg_pt_eight_five,pt_eight_five);
neg_pt_eight_five->pp->sign *= -1;
if ( rat_gt( *px, pt_eight_five, precision) || rat_lt( *px, neg_pt_eight_five, precision) )
{
PRAT ptmp = nullptr;
DUPRAT(ptmp,(*px));
mulrat(&ptmp, *px, precision);
addrat(&ptmp, rat_one, precision);
rootrat(&ptmp, rat_two, radix, precision);
addrat(px, ptmp, precision);
lograt(px, precision);
destroyrat(ptmp);
}
else
{
CREATETAYLOR();
xx->pp->sign *= -1;
DUPRAT(pret,(*px));
DUPRAT(thisterm,(*px));
DUPNUM(n2,num_one);
do
{
NEXTTERM(xx,MULNUM(n2) MULNUM(n2)
INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision);
}
while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
destroyrat(neg_pt_eight_five);
}
//-----------------------------------------------------------------------------
//
// FUNCTION: acoshrat
//
// ARGUMENTS: x PRAT representation of number to take the inverse
// hyperbolic cose of
// RETURN: acosh of x in PRAT form.
//
// EXPLANATION: This uses
//
// acosh(x)=ln(x+sqrt(x^2-1))
//
// For x >= 1
//
//-----------------------------------------------------------------------------
void acoshrat( PRAT *px, uint32_t radix, int32_t precision)
{
if ( rat_lt( *px, rat_one, precision) )
{
throw CALC_E_DOMAIN;
}
else
{
PRAT ptmp = nullptr;
DUPRAT(ptmp,(*px));
mulrat(&ptmp, *px, precision);
subrat(&ptmp, rat_one, precision);
rootrat(&ptmp,rat_two, radix, precision);
addrat(px, ptmp, precision);
lograt(px, precision);
destroyrat(ptmp);
}
}
//-----------------------------------------------------------------------------
//
// FUNCTION: atanhrat
//
// ARGUMENTS: x PRAT representation of number to take the inverse
// hyperbolic tangent of
//
// RETURN: atanh of x in PRAT form.
//
// EXPLANATION: This uses
//
// 1 x+1
// atanh(x) = -*ln(----)
// 2 x-1
//
//-----------------------------------------------------------------------------
void atanhrat( PRAT *px, int32_t precision)
{
PRAT ptmp = nullptr;
DUPRAT(ptmp,(*px));
subrat(&ptmp, rat_one, precision);
addrat(px, rat_one, precision);
divrat(px, ptmp, precision);
(*px)->pp->sign *= -1;
lograt(px, precision);
divrat(px, rat_two, precision);
destroyrat(ptmp);
}

View File

@@ -0,0 +1,219 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//---------------------------------------------------------------------------
// Package Title ratpak
// File num.c
// Copyright (C) 1995-99 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains routines for and, or, xor, not and other support
//
//---------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
void lshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
PRAT pwr= nullptr;
long intb;
intrat(pa, radix, precision);
if ( !zernum( (*pa)->pp ) )
{
// If input is zero we're done.
if ( rat_gt( b, rat_max_exp, precision) )
{
// Don't attempt lsh of anything big
throw( CALC_E_DOMAIN );
}
intb = rattolong(b, radix, precision);
DUPRAT(pwr,rat_two);
ratpowlong(&pwr, intb, precision);
mulrat(pa, pwr, precision);
destroyrat(pwr);
}
}
void rshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
PRAT pwr= nullptr;
long intb;
intrat(pa, radix, precision);
if ( !zernum( (*pa)->pp ) )
{
// If input is zero we're done.
if ( rat_lt( b, rat_min_exp, precision) )
{
// Don't attempt rsh of anything big and negative.
throw( CALC_E_DOMAIN );
}
intb = rattolong(b, radix, precision);
DUPRAT(pwr,rat_two);
ratpowlong(&pwr, intb, precision);
divrat(pa, pwr, precision);
destroyrat(pwr);
}
}
void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision);
void boolnum( PNUMBER *pa, PNUMBER b, int func );
enum {
FUNC_AND,
FUNC_OR,
FUNC_XOR
} BOOL_FUNCS;
void andrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
boolrat( pa, b, FUNC_AND, radix, precision);
}
void orrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
boolrat( pa, b, FUNC_OR, radix, precision);
}
void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
{
boolrat( pa, b, FUNC_XOR, radix, precision);
}
//---------------------------------------------------------------------------
//
// FUNCTION: boolrat
//
// ARGUMENTS: pointer to a rational a second rational.
//
// RETURN: None, changes pointer.
//
// DESCRIPTION: Does the rational equivalent of *pa op= b;
//
//---------------------------------------------------------------------------
void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision)
{
PRAT tmp= nullptr;
intrat( pa, radix, precision);
DUPRAT(tmp,b);
intrat( &tmp, radix, precision);
boolnum( &((*pa)->pp), tmp->pp, func );
destroyrat(tmp);
}
//---------------------------------------------------------------------------
//
// FUNCTION: boolnum
//
// ARGUMENTS: pointer to a number a second number
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa &= b.
// radix doesn't matter for logicals.
// WARNING: Assumes numbers are unsigned.
//
//---------------------------------------------------------------------------
void boolnum( PNUMBER *pa, PNUMBER b, int func )
{
PNUMBER c= nullptr;
PNUMBER a= nullptr;
MANTTYPE *pcha;
MANTTYPE *pchb;
MANTTYPE *pchc;
long cdigits;
long mexp;
MANTTYPE da;
MANTTYPE db;
a=*pa;
cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) -
min( a->exp, b->exp );
createnum( c, cdigits );
c->exp = min( a->exp, b->exp );
mexp = c->exp;
c->cdigit = cdigits;
pcha = a->mant;
pchb = b->mant;
pchc = c->mant;
for ( ;cdigits > 0; cdigits--, mexp++ )
{
da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp >
(c->cdigit - a->cdigit) ) ) ?
*pcha++ : 0 );
db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp >
(c->cdigit - b->cdigit) ) ) ?
*pchb++ : 0 );
switch ( func )
{
case FUNC_AND:
*pchc++ = da & db;
break;
case FUNC_OR:
*pchc++ = da | db;
break;
case FUNC_XOR:
*pchc++ = da ^ db;
break;
}
}
c->sign = a->sign;
while ( c->cdigit > 1 && *(--pchc) == 0 )
{
c->cdigit--;
}
destroynum( *pa );
*pa=c;
}
//-----------------------------------------------------------------------------
//
// FUNCTION: modrat
//
// ARGUMENTS: pointer to a rational a second rational.
//
// RETURN: None, changes pointer.
//
// DESCRIPTION: Does the rational equivalent of frac(*pa);
//
//-----------------------------------------------------------------------------
void modrat( PRAT *pa, PRAT b )
{
PRAT tmp = nullptr;
if ( zerrat( b ) )
{
throw CALC_E_INDEFINITE;
}
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 );
}

View File

@@ -0,0 +1,655 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File num.c
// Copyright (C) 1995-97 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains number routines for add, mul, div, rem and other support
// and longs.
//
// Special Information
//
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
using namespace std;
//----------------------------------------------------------------------------
//
// FUNCTION: addnum
//
// ARGUMENTS: pointer to a number a second number, and the
// radix.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa += b.
// Assumes radix is the base of both numbers.
//
// ALGORITHM: Adds each digit from least significant to most
// significant.
//
//
//----------------------------------------------------------------------------
void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix);
void __inline addnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
{
if ( b->cdigit > 1 || b->mant[0] != 0 )
{ // If b is zero we are done.
if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 0 )
{ // pa and b are both nonzero.
_addnum( pa, b, radix);
}
else
{ // if pa is zero and b isn't just copy b.
DUPNUM(*pa,b);
}
}
}
void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
{
PNUMBER c= nullptr; // c will contain the result.
PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa
MANTTYPE *pcha; // pcha is a pointer to the mantissa of a.
MANTTYPE *pchb; // pchb is a pointer to the mantissa of b.
MANTTYPE *pchc; // pchc is a pointer to the mantissa of c.
long cdigits; // cdigits is the max count of the digits results
// used as a counter.
long mexp; // mexp is the exponent of the result.
MANTTYPE da; // da is a single 'digit' after possible padding.
MANTTYPE db; // db is a single 'digit' after possible padding.
MANTTYPE cy=0; // cy is the value of a carry after adding two 'digits'
long fcompla = 0; // fcompla is a flag to signal a is negative.
long fcomplb = 0; // fcomplb is a flag to signal b is negative.
a=*pa;
// Calculate the overlap of the numbers after alignment, this includes
// necessary padding 0's
cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) -
min( a->exp, b->exp );
createnum( c, cdigits + 1 );
c->exp = min( a->exp, b->exp );
mexp = c->exp;
c->cdigit = cdigits;
pcha = a->mant;
pchb = b->mant;
pchc = c->mant;
// Figure out the sign of the numbers
if ( a->sign != b->sign )
{
cy = 1;
fcompla = ( a->sign == -1 );
fcomplb = ( b->sign == -1 );
}
// Loop over all the digits, real and 0 padded. Here we know a and b are
// aligned
for ( ;cdigits > 0; cdigits--, mexp++ )
{
// Get digit from a, taking padding into account.
da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp >
(c->cdigit - a->cdigit) ) ) ?
*pcha++ : 0 );
// Get digit from b, taking padding into account.
db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp >
(c->cdigit - b->cdigit) ) ) ?
*pchb++ : 0 );
// Handle complementing for a and b digit. Might be a better way, but
// haven't found it yet.
if ( fcompla )
{
da = (MANTTYPE)(radix) - 1 - da;
}
if ( fcomplb )
{
db = (MANTTYPE)(radix) - 1 - db;
}
// Update carry as necessary
cy = da + db + cy;
*pchc++ = (MANTTYPE)(cy % (MANTTYPE)radix);
cy /= (MANTTYPE)radix;
}
// Handle carry from last sum as extra digit
if ( cy && !(fcompla || fcomplb) )
{
*pchc++ = cy;
c->cdigit++;
}
// Compute sign of result
if ( !(fcompla || fcomplb) )
{
c->sign = a->sign;
}
else
{
if ( cy )
{
c->sign = 1;
}
else
{
// In this particular case an overflow or underflow has occoured
// and all the digits need to be complemented, at one time an
// attempt to handle this above was made, it turned out to be much
// slower on average.
c->sign = -1;
cy = 1;
for ( ( cdigits = c->cdigit ), (pchc = c->mant);
cdigits > 0;
cdigits-- )
{
cy = (MANTTYPE)radix - (MANTTYPE)1 - *pchc + cy;
*pchc++ = (MANTTYPE)( cy % (MANTTYPE)radix);
cy /= (MANTTYPE)radix;
}
}
}
// Remove leading zeroes, remember digits are in order of
// increasing significance. i.e. 100 would be 0,0,1
while ( c->cdigit > 1 && *(--pchc) == 0 )
{
c->cdigit--;
}
destroynum( *pa );
*pa=c;
}
//----------------------------------------------------------------------------
//
// FUNCTION: mulnum
//
// ARGUMENTS: pointer to a number a second number, and the
// radix.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa *= b.
// Assumes radix is the radix of both numbers. This algorithm is the
// same one you learned in gradeschool.
//
//----------------------------------------------------------------------------
void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix);
void __inline mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
{
if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
{ // If b is one we don't multiply exactly.
if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 )
{ // pa and b are both nonone.
_mulnum( pa, b, radix);
}
else
{ // if pa is one and b isn't just copy b, and adjust the sign.
long sign = (*pa)->sign;
DUPNUM(*pa,b);
(*pa)->sign *= sign;
}
}
else
{ // But we do have to set the sign.
(*pa)->sign *= b->sign;
}
}
void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
{
PNUMBER c= nullptr; // c will contain the result.
PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa
MANTTYPE *pcha; // pcha is a pointer to the mantissa of a.
MANTTYPE *pchb; // pchb is a pointer to the mantissa of b.
MANTTYPE *pchc; // pchc is a pointer to the mantissa of c.
MANTTYPE *pchcoffset; // pchcoffset, is the anchor location of the next
// single digit multiply partial result.
long iadigit = 0; // Index of digit being used in the first number.
long ibdigit = 0; // Index of digit being used in the second number.
MANTTYPE da = 0; // da is the digit from the fist number.
TWO_MANTTYPE cy = 0; // cy is the carry resulting from the addition of
// a multiplied row into the result.
TWO_MANTTYPE mcy = 0; // mcy is the resultant from a single
// multiply, AND the carry of that multiply.
long icdigit = 0; // Index of digit being calculated in final result.
a=*pa;
ibdigit = a->cdigit + b->cdigit - 1;
createnum( c, ibdigit + 1 );
c->cdigit = ibdigit;
c->sign = a->sign * b->sign;
c->exp = a->exp + b->exp;
pcha = a->mant;
pchcoffset = c->mant;
for ( iadigit = a->cdigit; iadigit > 0; iadigit-- )
{
da = *pcha++;
pchb = b->mant;
// Shift pchc, and pchcoffset, one for each digit
pchc = pchcoffset++;
for ( ibdigit = b->cdigit; ibdigit > 0; ibdigit-- )
{
cy = 0;
mcy = (TWO_MANTTYPE)da * *pchb;
if ( mcy )
{
icdigit = 0;
if ( ibdigit == 1 && iadigit == 1 )
{
c->cdigit++;
}
}
// If result is nonzero, or while result of carry is nonzero...
while ( mcy || cy )
{
// update carry from addition(s) and multiply.
cy += (TWO_MANTTYPE)pchc[icdigit]+(mcy%(TWO_MANTTYPE)radix);
// update result digit from
pchc[icdigit++]=(MANTTYPE)(cy%(TWO_MANTTYPE)radix);
// update carries from
mcy /= (TWO_MANTTYPE)radix;
cy /= (TWO_MANTTYPE)radix;
}
pchb++;
pchc++;
}
}
// prevent different kinds of zeros, by stripping leading duplicate zeroes.
// digits are in order of increasing significance.
while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 )
{
c->cdigit--;
}
destroynum( *pa );
*pa=c;
}
//----------------------------------------------------------------------------
//
// FUNCTION: remnum
//
// ARGUMENTS: pointer to a number a second number, and the
// radix.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa %= b.
// Repeatedly subtracts off powers of 2 of b until *pa < b.
//
//
//----------------------------------------------------------------------------
void remnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
{
PNUMBER tmp = nullptr; // tmp is the working remainder.
PNUMBER lasttmp = nullptr; // lasttmp is the last remainder which worked.
// Once *pa is less than b, *pa is the remainder.
while ( !lessnum( *pa, b ) )
{
DUPNUM( tmp, b );
if ( lessnum( tmp, *pa ) )
{
// Start off close to the right answer for subtraction.
tmp->exp = (*pa)->cdigit+(*pa)->exp - tmp->cdigit;
if ( MSD(*pa) <= MSD(tmp) )
{
// Don't take the chance that the numbers are equal.
tmp->exp--;
}
}
destroynum( lasttmp );
lasttmp=longtonum( 0, radix);
while ( lessnum( tmp, *pa ) )
{
DUPNUM( lasttmp, tmp );
addnum( &tmp, tmp, radix);
}
if ( lessnum( *pa, tmp ) )
{
// too far, back up...
destroynum( tmp );
tmp=lasttmp;
lasttmp= nullptr;
}
// Subtract the working remainder from the remainder holder.
tmp->sign = -1*(*pa)->sign;
addnum( pa, tmp, radix);
destroynum( tmp );
destroynum( lasttmp );
}
}
//---------------------------------------------------------------------------
//
// FUNCTION: divnum
//
// ARGUMENTS: pointer to a number a second number, and the
// radix.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the number equivalent of *pa /= b.
// Assumes radix is the radix of both numbers.
//
//---------------------------------------------------------------------------
void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision);
void __inline divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision)
{
if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
{
// b is not one
_divnum( pa, b, radix, precision);
}
else
{ // But we do have to set the sign.
(*pa)->sign *= b->sign;
}
}
void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision)
{
PNUMBER a = *pa;
long thismax = precision + 2;
if (thismax < a->cdigit)
{
thismax = a->cdigit;
}
if (thismax < b->cdigit)
{
thismax = b->cdigit;
}
PNUMBER c = nullptr;
createnum(c, thismax + 1);
c->exp = (a->cdigit + a->exp) - (b->cdigit + b->exp) + 1;
c->sign = a->sign * b->sign;
MANTTYPE *ptrc = c->mant + thismax;
PNUMBER rem = nullptr;
PNUMBER tmp = nullptr;
DUPNUM(rem, a);
DUPNUM(tmp, b);
tmp->sign = a->sign;
rem->exp = b->cdigit + b->exp - rem->cdigit;
// Build a table of multiplications of the divisor, this is quicker for
// more than radix 'digits'
list<PNUMBER> numberList{ longtonum(0L, radix) };
for (unsigned long i = 1; i < radix; i++)
{
PNUMBER newValue = nullptr;
DUPNUM(newValue, numberList.front());
addnum(&newValue, tmp, radix);
numberList.emplace_front(newValue);
}
destroynum(tmp);
long digit;
long cdigits = 0;
while (cdigits++ < thismax && !zernum(rem))
{
digit = radix - 1;
PNUMBER multiple = nullptr;
for (const auto& num : numberList)
{
if (!lessnum(rem, num) || !--digit)
{
multiple = num;
break;
}
}
if (digit)
{
multiple->sign *= -1;
addnum(&rem, multiple, radix);
multiple->sign *= -1;
}
rem->exp++;
*ptrc-- = (MANTTYPE)digit;
}
cdigits--;
if (c->mant != ++ptrc)
{
memmove(c->mant, ptrc, (int)(cdigits * sizeof(MANTTYPE)));
}
// Cleanup table structure
for (auto& num : numberList)
{
destroynum(num);
}
if (!cdigits)
{
c->cdigit = 1;
c->exp = 0;
}
else
{
c->cdigit = cdigits;
c->exp -= cdigits;
while (c->cdigit > 1 && c->mant[c->cdigit - 1] == 0)
{
c->cdigit--;
}
}
destroynum(rem);
destroynum(*pa);
*pa = c;
}
//---------------------------------------------------------------------------
//
// FUNCTION: equnum
//
// ARGUMENTS: two numbers.
//
// RETURN: Boolean
//
// DESCRIPTION: Does the number equivalent of ( a == b )
// Only assumes that a and b are the same radix.
//
//---------------------------------------------------------------------------
bool equnum( PNUMBER a, PNUMBER b )
{
long diff;
MANTTYPE *pa;
MANTTYPE *pb;
long cdigits;
long ccdigits;
MANTTYPE da;
MANTTYPE db;
diff = ( a->cdigit + a->exp ) - ( b->cdigit + b->exp );
if ( diff < 0 )
{
// If the exponents are different, these are different numbers.
return false;
}
else
{
if ( diff > 0 )
{
// If the exponents are different, these are different numbers.
return false;
}
else
{
// OK the exponents match.
pa = a->mant;
pb = b->mant;
pa += a->cdigit - 1;
pb += b->cdigit - 1;
cdigits = max( a->cdigit, b->cdigit );
ccdigits = cdigits;
// Loop over all digits until we run out of digits or there is a
// difference in the digits.
for ( ;cdigits > 0; cdigits-- )
{
da = ( (cdigits > (ccdigits - a->cdigit) ) ?
*pa-- : 0 );
db = ( (cdigits > (ccdigits - b->cdigit) ) ?
*pb-- : 0 );
if ( da != db )
{
return false;
}
}
// In this case, they are equal.
return true;
}
}
}
//---------------------------------------------------------------------------
//
// FUNCTION: lessnum
//
// ARGUMENTS: two numbers.
//
// RETURN: Boolean
//
// DESCRIPTION: Does the number equivalent of ( abs(a) < abs(b) )
// Only assumes that a and b are the same radix, WARNING THIS IS AN.
// UNSIGNED COMPARE!
//
//---------------------------------------------------------------------------
bool lessnum( PNUMBER a, PNUMBER b )
{
long diff;
MANTTYPE *pa;
MANTTYPE *pb;
long cdigits;
long ccdigits;
MANTTYPE da;
MANTTYPE db;
diff = ( a->cdigit + a->exp ) - ( b->cdigit + b->exp );
if ( diff < 0 )
{
// The exponent of a is less than b
return true;
}
else
{
if ( diff > 0 )
{
return false;
}
else
{
pa = a->mant;
pb = b->mant;
pa += a->cdigit - 1;
pb += b->cdigit - 1;
cdigits = max( a->cdigit, b->cdigit );
ccdigits = cdigits;
for ( ;cdigits > 0; cdigits-- )
{
da = ( (cdigits > (ccdigits - a->cdigit) ) ?
*pa-- : 0 );
db = ( (cdigits > (ccdigits - b->cdigit) ) ?
*pb-- : 0 );
diff = da-db;
if ( diff )
{
return( diff < 0 );
}
}
// In this case, they are equal.
return false;
}
}
}
//----------------------------------------------------------------------------
//
// FUNCTION: zernum
//
// ARGUMENTS: number
//
// RETURN: Boolean
//
// DESCRIPTION: Does the number equivalent of ( !a )
//
//----------------------------------------------------------------------------
bool zernum( PNUMBER a )
{
long length;
MANTTYPE *pcha;
length = a->cdigit;
pcha = a->mant;
// loop over all the digits until you find a nonzero or until you run
// out of digits
while ( length-- > 0 )
{
if ( *pcha++ )
{
// One of the digits isn't zero, therefore the number isn't zero
return false;
}
}
// All of the digits are zero, therefore the number is zero
return true;
}

View File

@@ -0,0 +1,303 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File rat.c
// Copyright (C) 1995-96 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains mul, div, add, and other support functions for rationals.
//
//
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
using namespace std;
//-----------------------------------------------------------------------------
//
// FUNCTION: gcdrat
//
// ARGUMENTS: pointer to a rational.
//
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Divides p and q in rational by the G.C.D.
// of both. It was hoped this would speed up some
// calculations, and until the above trimming was done it
// did, but after trimming gcdratting, only slows things
// down.
//
//-----------------------------------------------------------------------------
void gcdrat( PRAT *pa, uint32_t radix, int32_t precision)
{
PNUMBER pgcd= nullptr;
PRAT a= nullptr;
a=*pa;
pgcd = gcd( a->pp, a->pq );
if ( !zernum( pgcd ) )
{
divnumx( &(a->pp), pgcd, precision);
divnumx( &(a->pq), pgcd, precision);
}
destroynum( pgcd );
*pa=a;
RENORMALIZE(*pa);
}
//-----------------------------------------------------------------------------
//
// FUNCTION: fracrat
//
// ARGUMENTS: pointer to a rational a second rational.
//
// RETURN: None, changes pointer.
//
// DESCRIPTION: Does the rational equivalent of frac(*pa);
//
//-----------------------------------------------------------------------------
void fracrat( PRAT *pa , uint32_t radix, int32_t precision)
{
// Only do the intrat operation if number is nonzero.
// and only if the bottom part is not one.
if ( !zernum( (*pa)->pp ) && !equnum( (*pa)->pq, num_one ) )
{
wstring ratStr = RatToString(*pa, FMT_FLOAT, radix, precision);
PNUMBER pnum = StringToNumber(ratStr, radix, precision);
destroyrat( *pa );
*pa = numtorat( pnum, radix);
destroynum( pnum );
}
remnum( &((*pa)->pp), (*pa)->pq, BASEX );
//Get *pa back in the integer over integer form.
RENORMALIZE(*pa);
}
//-----------------------------------------------------------------------------
//
// FUNCTION: mulrat
//
// ARGUMENTS: pointer to a rational a second rational.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the rational equivalent of *pa *= b.
// Assumes radix is the radix of both numbers.
//
//-----------------------------------------------------------------------------
void mulrat( PRAT *pa, PRAT b, int32_t precision)
{
// Only do the multiply if it isn't zero.
if ( !zernum( (*pa)->pp ) )
{
mulnumx( &((*pa)->pp), b->pp );
mulnumx( &((*pa)->pq), b->pq );
trimit(pa, precision);
}
else
{
// If it is zero, blast a one in the denominator.
DUPNUM( ((*pa)->pq), num_one );
}
#ifdef MULGCD
gcdrat( pa );
#endif
}
//-----------------------------------------------------------------------------
//
// FUNCTION: divrat
//
// ARGUMENTS: pointer to a rational a second rational.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the rational equivalent of *pa /= b.
// Assumes radix is the radix of both numbers.
//
//-----------------------------------------------------------------------------
void divrat( PRAT *pa, PRAT b, int32_t precision)
{
if ( !zernum( (*pa)->pp ) )
{
// Only do the divide if the top isn't zero.
mulnumx( &((*pa)->pp), b->pq );
mulnumx( &((*pa)->pq), b->pp );
if ( zernum( (*pa)->pq ) )
{
// raise an exception if the bottom is 0.
throw( CALC_E_DIVIDEBYZERO );
}
trimit(pa, precision);
}
else
{
// Top is zero.
if ( zerrat( b ) )
{
// If bottom is zero
// 0 / 0 is indefinite, raise an exception.
throw( CALC_E_INDEFINITE );
}
else
{
// 0/x make a unique 0.
DUPNUM( ((*pa)->pq), num_one );
}
}
#ifdef DIVGCD
gcdrat( pa );
#endif
}
//-----------------------------------------------------------------------------
//
// FUNCTION: subrat
//
// ARGUMENTS: pointer to a rational a second rational.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the rational equivalent of *pa += b.
// Assumes base is internal througought.
//
//-----------------------------------------------------------------------------
void subrat( PRAT *pa, PRAT b, int32_t precision)
{
b->pp->sign *= -1;
addrat( pa, b, precision);
b->pp->sign *= -1;
}
//-----------------------------------------------------------------------------
//
// FUNCTION: addrat
//
// ARGUMENTS: pointer to a rational a second rational.
//
// RETURN: None, changes first pointer.
//
// DESCRIPTION: Does the rational equivalent of *pa += b.
// Assumes base is internal througought.
//
//-----------------------------------------------------------------------------
void addrat( PRAT *pa, PRAT b, int32_t precision)
{
PNUMBER bot= nullptr;
if ( equnum( (*pa)->pq, b->pq ) )
{
// Very special case, q's match.,
// make sure signs are involved in the calculation
// we have to do this since the optimization here is only
// working with the top half of the rationals.
(*pa)->pp->sign *= (*pa)->pq->sign;
(*pa)->pq->sign = 1;
b->pp->sign *= b->pq->sign;
b->pq->sign = 1;
addnum( &((*pa)->pp), b->pp, BASEX );
}
else
{
// Usual case q's aren't the same.
DUPNUM( bot, (*pa)->pq );
mulnumx( &bot, b->pq );
mulnumx( &((*pa)->pp), b->pq );
mulnumx( &((*pa)->pq), b->pp );
addnum( &((*pa)->pp), (*pa)->pq, BASEX );
destroynum( (*pa)->pq );
(*pa)->pq = bot;
trimit(pa, precision);
// Get rid of negative zeroes here.
(*pa)->pp->sign *= (*pa)->pq->sign;
(*pa)->pq->sign = 1;
}
#ifdef ADDGCD
gcdrat( pa );
#endif
}
//-----------------------------------------------------------------------------
//
// FUNCTION: rootrat
//
// PARAMETERS: y prat representation of number to take the root of
// n prat representation of the root to take.
//
// RETURN: bth root of a in rat form.
//
// EXPLANATION: This is now a stub function to powrat().
//
//-----------------------------------------------------------------------------
void rootrat( PRAT *py, PRAT n, uint32_t radix, int32_t precision)
{
// Initialize 1/n
PRAT oneovern= nullptr;
DUPRAT(oneovern,rat_one);
divrat(&oneovern, n, precision);
powrat(py, oneovern, radix, precision);
destroyrat(oneovern);
}
//-----------------------------------------------------------------------------
//
// FUNCTION: zerrat
//
// ARGUMENTS: Rational number.
//
// RETURN: Boolean
//
// DESCRIPTION: Returns true if input is zero.
// False otherwise.
//
//-----------------------------------------------------------------------------
bool zerrat( PRAT a )
{
return( zernum(a->pp) );
}

View File

@@ -0,0 +1,482 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Autogenerated by _dumprawrat in support.c
NUMBER init_num_one= {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_num_two= {
1,
1,
0,
{ 2,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_num_five= {
1,
1,
0,
{ 5,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_num_six= {
1,
1,
0,
{ 6,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_num_ten= {
1,
1,
0,
{ 10,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_smallest = {
1,
1,
0,
{ 1,}
};
NUMBER init_q_rat_smallest = {
1,
4,
0,
{ 0, 190439170, 901055854, 10097,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_negsmallest = {
-1,
1,
0,
{ 1,}
};
NUMBER init_q_rat_negsmallest = {
1,
4,
0,
{ 0, 190439170, 901055854, 10097,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_pt_eight_five = {
1,
1,
0,
{ 85,}
};
NUMBER init_q_pt_eight_five = {
1,
1,
0,
{ 100,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_six = {
1,
1,
0,
{ 6,}
};
NUMBER init_q_rat_six = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_two = {
1,
1,
0,
{ 2,}
};
NUMBER init_q_rat_two = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_zero = {
1,
1,
0,
{ 0,}
};
NUMBER init_q_rat_zero = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_one = {
1,
1,
0,
{ 1,}
};
NUMBER init_q_rat_one = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_neg_one = {
-1,
1,
0,
{ 1,}
};
NUMBER init_q_rat_neg_one = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_half = {
1,
1,
0,
{ 1,}
};
NUMBER init_q_rat_half = {
1,
1,
0,
{ 2,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_ten = {
1,
1,
0,
{ 10,}
};
NUMBER init_q_rat_ten = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_pi = {
1,
6,
0,
{ 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,}
};
NUMBER init_q_pi = {
1,
6,
0,
{ 1288380402, 1120116153, 1860424692, 1944118326, 1583591604, 2,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_two_pi = {
1,
6,
0,
{ 251055792, 567796700, 1773504224, 1198217877, 428852897, 17,}
};
NUMBER init_q_two_pi = {
1,
6,
0,
{ 1288380402, 1120116153, 1860424692, 1944118326, 1583591604, 2,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_pi_over_two = {
1,
6,
0,
{ 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,}
};
NUMBER init_q_pi_over_two = {
1,
6,
0,
{ 429277156, 92748659, 1573365737, 1740753005, 1019699561, 5,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_one_pt_five_pi = {
1,
6,
0,
{ 1241201312, 270061909, 1051574664, 1924965045, 1340320627, 70,}
};
NUMBER init_q_one_pt_five_pi = {
1,
6,
0,
{ 1579671539, 1837970263, 1067644340, 523549916, 2119366659, 14,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_e_to_one_half = {
1,
6,
0,
{ 256945612, 216219427, 223516738, 477442596, 581063757, 23,}
};
NUMBER init_q_e_to_one_half = {
1,
6,
0,
{ 1536828363, 698484484, 1127331835, 224219346, 245499408, 14,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_exp = {
1,
6,
0,
{ 943665199, 1606559160, 1094967530, 1759391384, 1671799163, 1123581,}
};
NUMBER init_q_rat_exp = {
1,
6,
0,
{ 879242208, 2022880100, 617392930, 1374929092, 1367479163, 413342,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_ln_ten = {
1,
6,
0,
{ 2086268922, 165794492, 1416063951, 1851428830, 1893239400, 65366841,}
};
NUMBER init_q_ln_ten = {
1,
6,
0,
{ 26790652, 564532679, 783998273, 216030448, 1564709968, 28388458,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_ln_two = {
1,
6,
0,
{ 1789230241, 1057927868, 715399197, 908801241, 1411265331, 3,}
};
NUMBER init_q_ln_two = {
1,
6,
0,
{ 1559869847, 1930657510, 1228561531, 219003871, 593099283, 5,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rad_to_deg = {
1,
6,
0,
{ 2127722024, 1904928383, 2016479213, 2048947859, 1578647346, 492,}
};
NUMBER init_q_rad_to_deg = {
1,
6,
0,
{ 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rad_to_grad = {
1,
6,
0,
{ 2125526288, 684931327, 570267400, 129125085, 1038224725, 547,}
};
NUMBER init_q_rad_to_grad = {
1,
6,
0,
{ 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_qword = {
1,
3,
0,
{ 2147483647, 2147483647, 3,}
};
NUMBER init_q_rat_qword = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_dword = {
1,
2,
0,
{ 2147483647, 1,}
};
NUMBER init_q_rat_dword = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_max_long = {
1,
1,
0,
{ 2147483647,}
};
NUMBER init_q_rat_max_long = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_min_long = {
-1,
2,
0,
{ 0, 1,}
};
NUMBER init_q_rat_min_long = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_word = {
1,
1,
0,
{ 65535,}
};
NUMBER init_q_rat_word = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_byte = {
1,
1,
0,
{ 255,}
};
NUMBER init_q_rat_byte = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_400 = {
1,
1,
0,
{ 400,}
};
NUMBER init_q_rat_400 = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_360 = {
1,
1,
0,
{ 360,}
};
NUMBER init_q_rat_360 = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_200 = {
1,
1,
0,
{ 200,}
};
NUMBER init_q_rat_200 = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_180 = {
1,
1,
0,
{ 180,}
};
NUMBER init_q_rat_180 = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_max_exp = {
1,
1,
0,
{ 100000,}
};
NUMBER init_q_rat_max_exp = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_min_exp = {
-1,
1,
0,
{ 100000,}
};
NUMBER init_q_rat_min_exp = {
1,
1,
0,
{ 1,}
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_max_fact = {
1,
1,
0,
{ 3249, }
};
NUMBER init_q_rat_max_fact = {
1,
1,
0,
{ 1, }
};
// Autogenerated by _dumprawrat in support.c
NUMBER init_p_rat_min_fact = {
-1,
1,
0,
{ 1000, }
};
NUMBER init_q_rat_min_fact = {
1,
1,
0,
{ 1, }
};

View File

@@ -0,0 +1,452 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
//-----------------------------------------------------------------------------
// Package Title ratpak
// File ratpak.h
// Copyright (C) 1995-99 Microsoft
// Date 01-16-95
//
//
// Description
//
// Infinite precision math package header file, if you use ratpak.lib you
// need to include this header.
//
//-----------------------------------------------------------------------------
#include "CalcErr.h"
static constexpr uint32_t BASEXPWR = 31L;// Internal log2(BASEX)
static constexpr uint32_t BASEX = 0x80000000; // Internal radix used in calculations, hope to raise
// this to 2^32 after solving scaling problems with
// overflow detection esp. in mul
typedef unsigned long MANTTYPE;
typedef unsigned __int64 TWO_MANTTYPE;
enum eNUMOBJ_FMT {
FMT_FLOAT, // returns floating point, or exponential if number is too big
FMT_SCIENTIFIC, // always returns scientific notation
FMT_ENGINEERING // always returns engineering notation such that exponent is a multiple of 3
};
enum eANGLE_TYPE {
ANGLE_DEG, // Calculate trig using 360 degrees per revolution
ANGLE_RAD, // Calculate trig using 2 pi radians per revolution
ANGLE_GRAD // Calculate trig using 400 gradients per revolution
};
typedef enum eNUMOBJ_FMT NUMOBJ_FMT;
typedef enum eANGLE_TYPE ANGLE_TYPE;
//-----------------------------------------------------------------------------
//
// NUMBER type is a representation of a generic sized generic radix number
//
//-----------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable:4200) // nonstandard extension used : zero-sized array in struct/union
typedef struct _number
{
long sign; // The sign of the mantissa, +1, or -1
long cdigit; // The number of digits, or what passes for digits in the
// radix being used.
long exp; // The offset of digits from the radix point
// (decimal point in radix 10)
MANTTYPE mant[];
// This is actually allocated as a continuation of the
// NUMBER structure.
} NUMBER, *PNUMBER, **PPNUMBER;
#pragma warning(pop)
//-----------------------------------------------------------------------------
//
// RAT type is a representation radix on 2 NUMBER types.
// pp/pq, where pp and pq are pointers to integral NUMBER types.
//
//-----------------------------------------------------------------------------
typedef struct _rat
{
PNUMBER pp;
PNUMBER pq;
} RAT, *PRAT;
static constexpr uint32_t MAX_LONG_SIZE = 33; // Base 2 requires 32 'digits'
//-----------------------------------------------------------------------------
//
// List of useful constants for evaluation, note this list needs to be
// initialized.
//
//-----------------------------------------------------------------------------
extern PNUMBER num_one;
extern PNUMBER num_two;
extern PNUMBER num_five;
extern PNUMBER num_six;
extern PNUMBER num_ten;
extern PRAT ln_ten;
extern PRAT ln_two;
extern PRAT rat_zero;
extern PRAT rat_neg_one;
extern PRAT rat_one;
extern PRAT rat_two;
extern PRAT rat_six;
extern PRAT rat_half;
extern PRAT rat_ten;
extern PRAT pt_eight_five;
extern PRAT pi;
extern PRAT pi_over_two;
extern PRAT two_pi;
extern PRAT one_pt_five_pi;
extern PRAT e_to_one_half;
extern PRAT rat_exp;
extern PRAT rad_to_deg;
extern PRAT rad_to_grad;
extern PRAT rat_qword;
extern PRAT rat_dword;
extern PRAT rat_word;
extern PRAT rat_byte;
extern PRAT rat_360;
extern PRAT rat_400;
extern PRAT rat_180;
extern PRAT rat_200;
extern PRAT rat_nRadix;
extern PRAT rat_smallest;
extern PRAT rat_negsmallest;
extern PRAT rat_max_exp;
extern PRAT rat_min_exp;
extern PRAT rat_max_fact;
extern PRAT rat_min_fact;
extern PRAT rat_max_long;
extern PRAT rat_min_long;
// DUPNUM Duplicates a number taking care of allocation and internals
#define DUPNUM(a,b) destroynum(a);createnum( a, (b)->cdigit );_dupnum(a, b);
// DUPRAT Duplicates a rational taking care of allocation and internals
#define DUPRAT(a,b) destroyrat(a);createrat(a);DUPNUM((a)->pp,(b)->pp);DUPNUM((a)->pq,(b)->pq);
// LOG*RADIX calculates the integral portion of the log of a number in
// the base currently being used, only accurate to within g_ratio
#define LOGNUMRADIX(pnum) (((pnum)->cdigit+(pnum)->exp)*g_ratio)
#define LOGRATRADIX(prat) (LOGNUMRADIX((prat)->pp)-LOGNUMRADIX((prat)->pq))
// LOG*2 calculates the integral portion of the log of a number in
// the internal base being used, only accurate to within g_ratio
#define LOGNUM2(pnum) ((pnum)->cdigit+(pnum)->exp)
#define LOGRAT2(prat) (LOGNUM2((prat)->pp)-LOGNUM2((prat)->pq))
#if defined( DEBUG_RATPAK )
//-----------------------------------------------------------------------------
//
// Debug versions of rational number creation and destruction routines.
// used for debugging allocation errors.
//
//-----------------------------------------------------------------------------
#define createrat(y) (y)=_createrat(); \
{ \
std::wstringstream outputString; \
outputString << "createrat " << y << " " << # y << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \
OutputDebugString(outputString.str().c_str()); \
}
#define destroyrat(x) \
{ \
std::wstringstream outputString; \
outputString << "destroyrat " << x << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \
OutputDebugString(outputString.str().c_str()); \
} \
_destroyrat(x),(x)=nullptr
#define createnum(y,x) (y)=_createnum(x); \
{ \
std::wstringstream outputString; \
outputString << "createnum " << y << " " << # y << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \
OutputDebugString(outputString.str().c_str()); \
}
#define destroynum(x) \
{ \
std::wstringstream outputString; \
outputString << "destroynum " << x << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \
OutputDebugString(outputString.str().c_str()); \
} \
_destroynum(x),(x)=nullptr
#else
#define createrat(y) (y)=_createrat()
#define destroyrat(x) _destroyrat(x),(x)=nullptr
#define createnum(y,x) (y)=_createnum(x)
#define destroynum(x) _destroynum(x),(x)=nullptr
#endif
//-----------------------------------------------------------------------------
//
// Defines for checking when to stop taylor series expansions due to
// precision satisfaction.
//
//-----------------------------------------------------------------------------
// RENORMALIZE, gets the exponents non-negative.
#define RENORMALIZE(x) if ( (x)->pp->exp < 0 ) { \
(x)->pq->exp -= (x)->pp->exp; \
(x)->pp->exp = 0; \
} \
if ( (x)->pq->exp < 0 ) { \
(x)->pp->exp -= (x)->pq->exp; \
(x)->pq->exp = 0; \
}
// TRIMNUM ASSUMES the number is in radix form NOT INTERNAL BASEX!!!
#define TRIMNUM(x, precision) if ( !g_ftrueinfinite ) { \
long trim = (x)->cdigit - precision-g_ratio;\
if ( trim > 1 ) \
{ \
memmove( (x)->mant, &((x)->mant[trim]), sizeof(MANTTYPE)*((x)->cdigit-trim) ); \
(x)->cdigit -= trim; \
(x)->exp += trim; \
} \
}
// TRIMTOP ASSUMES the number is in INTERNAL BASEX!!!
#define TRIMTOP(x, precision) if ( !g_ftrueinfinite ) { \
long trim = (x)->pp->cdigit - (precision/g_ratio) - 2;\
if ( trim > 1 ) \
{ \
memmove( (x)->pp->mant, &((x)->pp->mant[trim]), sizeof(MANTTYPE)*((x)->pp->cdigit-trim) ); \
(x)->pp->cdigit -= trim; \
(x)->pp->exp += trim; \
} \
trim = min((x)->pp->exp,(x)->pq->exp);\
(x)->pp->exp -= trim;\
(x)->pq->exp -= trim;\
}
#define SMALL_ENOUGH_RAT(a, precision) (zernum((a)->pp) || ( ( ( (a)->pq->cdigit + (a)->pq->exp ) - ( (a)->pp->cdigit + (a)->pp->exp ) - 1 ) * g_ratio > precision ) )
//-----------------------------------------------------------------------------
//
// Defines for setting up taylor series expansions for infinite precision
// functions.
//
//-----------------------------------------------------------------------------
#define CREATETAYLOR() PRAT xx=nullptr;\
PNUMBER n2=nullptr; \
PRAT pret=nullptr; \
PRAT thisterm=nullptr; \
DUPRAT(xx,*px); \
mulrat(&xx,*px, precision); \
createrat(pret); \
pret->pp=longtonum( 0L, BASEX ); \
pret->pq=longtonum( 0L, BASEX );
#define DESTROYTAYLOR() destroynum( n2 ); \
destroyrat( xx );\
destroyrat( thisterm );\
destroyrat( *px );\
trimit(&pret, precision);\
*px=pret;
// INC(a) is the rational equivalent of a++
// Check to see if we can avoid doing this the hard way.
#define INC(a) if ( (a)->mant[0] < BASEX - 1 ) \
{ \
(a)->mant[0]++; \
} \
else \
{ \
addnum( &(a), num_one, BASEX); \
}
#define MSD(x) ((x)->mant[(x)->cdigit-1])
// MULNUM(b) is the rational equivalent of thisterm *= b where thisterm is
// a rational and b is a number, NOTE this is a mixed type operation for
// efficiency reasons.
#define MULNUM(b) mulnumx( &(thisterm->pp), b);
// DIVNUM(b) is the rational equivalent of thisterm /= b where thisterm is
// a rational and b is a number, NOTE this is a mixed type operation for
// efficiency reasons.
#define DIVNUM(b) mulnumx( &(thisterm->pq), b);
// NEXTTERM(p,d) is the rational equivalent of
// thisterm *= p
// d <d is usually an expansion of operations to get thisterm updated.>
// pret += thisterm
#define NEXTTERM(p,d,precision) mulrat(&thisterm,p,precision);d addrat( &pret, thisterm, precision )
//-----------------------------------------------------------------------------
//
// External variables used in the math package.
//
//-----------------------------------------------------------------------------
extern bool g_ftrueinfinite; // set to true to allow infinite precision
// don't use unless you know what you are doing
// used to help decide when to stop calculating.
extern long g_ratio; // Internally calculated ratio of internal radix
//-----------------------------------------------------------------------------
//
// External functions defined in the math package.
//
//-----------------------------------------------------------------------------
// Call whenever decimal separator character changes.
extern void SetDecimalSeparator(wchar_t decimalSeparator);
// Call whenever either radix or precision changes, is smarter about recalculating constants.
extern void ChangeConstants(uint32_t radix, int32_t precision);
extern bool equnum(_In_ PNUMBER a, _In_ PNUMBER b ); // returns true of a == b
extern bool lessnum(_In_ PNUMBER a, _In_ PNUMBER b ); // returns true of a < b
extern bool zernum(_In_ PNUMBER a ); // returns true of a == 0
extern bool zerrat(_In_ PRAT a ); // returns true if a == 0/q
extern std::wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_t precision);
// returns a text representation of a PRAT
extern std::wstring RatToString(_Inout_ PRAT& prat, int format, uint32_t radix, int32_t precision);
extern long numtolong(_In_ PNUMBER pnum, uint32_t radix );
extern long rattolong(_In_ PRAT prat, uint32_t radix, int32_t precision);
ULONGLONG rattoUlonglong(_In_ PRAT prat, uint32_t radix, int32_t precision);
extern PNUMBER _createnum(_In_ ULONG size ); // returns an empty number structure with size digits
extern PNUMBER nRadixxtonum(_In_ PNUMBER a, uint32_t radix, int32_t precision);
extern PNUMBER gcd(_In_ PNUMBER a, _In_ PNUMBER b );
extern PNUMBER StringToNumber(std::wstring_view numberString, uint32_t radix, int32_t precision); // takes a text representation of a number and returns a number.
// takes a text representation of a number as a mantissa with sign and an exponent with sign.
extern PRAT StringToRat(bool mantissaIsNegative, std::wstring_view mantissa, bool exponentIsNegative, std::wstring_view exponent, uint32_t radix, int32_t precision);
extern PNUMBER longfactnum(long inlong, uint32_t radix);
extern PNUMBER longprodnum(long start, long stop, uint32_t radix);
extern PNUMBER longtonum(long inlong, uint32_t radix);
extern PNUMBER Ulongtonum(unsigned long inlong, uint32_t radix);
extern PNUMBER numtonRadixx(PNUMBER a, uint32_t radix);
// creates a empty/undefined rational representation (p/q)
extern PRAT _createrat( void );
// returns a new rat structure with the acos of x->p/x->q taking into account
// angle type
extern void acosanglerat( _Inout_ PRAT *px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
// returns a new rat structure with the acosh of x->p/x->q
extern void acoshrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the acos of x->p/x->q
extern void acosrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the asin of x->p/x->q taking into account
// angle type
extern void asinanglerat( _Inout_ PRAT *px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void asinhrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the asinh of x->p/x->q
// returns a new rat structure with the asin of x->p/x->q
extern void asinrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the atan of x->p/x->q taking into account
// angle type
extern void atananglerat( _Inout_ PRAT *px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
// returns a new rat structure with the atanh of x->p/x->q
extern void atanhrat( _Inout_ PRAT *px, int32_t precision);
// returns a new rat structure with the atan of x->p/x->q
extern void atanrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the cosh of x->p/x->q
extern void coshrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the cos of x->p/x->q
extern void cosrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the cos of x->p/x->q taking into account
// angle type
extern void cosanglerat( _Inout_ PRAT *px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
// returns a new rat structure with the exp of x->p/x->q this should not be called explicitly.
extern void _exprat( _Inout_ PRAT *px, int32_t precision);
// returns a new rat structure with the exp of x->p/x->q
extern void exprat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the log base 10 of x->p/x->q
extern void log10rat( _Inout_ PRAT *px, int32_t precision);
// returns a new rat structure with the natural log of x->p/x->q
extern void lograt( _Inout_ PRAT *px, int32_t precision);
extern PRAT longtorat( long inlong );
extern PRAT Ulongtorat( unsigned long inulong );
extern PRAT numtorat( _In_ PNUMBER pin, uint32_t radix);
extern void sinhrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
extern void sinrat( _Inout_ PRAT *px );
// returns a new rat structure with the sin of x->p/x->q taking into account
// angle type
extern void sinanglerat( _Inout_ PRAT *px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void tanhrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
extern void tanrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
// returns a new rat structure with the tan of x->p/x->q taking into account
// angle type
extern void tananglerat( _Inout_ PRAT *px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void _dupnum(_In_ PNUMBER dest, _In_ PNUMBER src);
extern void _destroynum( _In_ PNUMBER pnum );
extern void _destroyrat( _In_ PRAT prat );
extern void addnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix);
extern void addrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision);
extern void andrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision);
extern void divnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix, int32_t precision);
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 gcdrat( _Inout_ PRAT *pa, uint32_t radix, 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);
extern void mulnumx( _Inout_ PNUMBER *pa, _In_ PNUMBER b );
extern void mulrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision);
extern void numpowlong( _Inout_ PNUMBER *proot, long power, uint32_t radix, int32_t precision);
extern void numpowlongx( _Inout_ PNUMBER *proot, long power );
extern void orrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision);
extern void powrat( _Inout_ PRAT *pa, _In_ PRAT b , uint32_t radix, int32_t precision);
extern void powratNumeratorDenominator(_Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision);
extern void powratcomp(_Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision);
extern void ratpowlong( _Inout_ PRAT *proot, long power, int32_t precision);
extern void remnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix);
extern void rootrat( _Inout_ PRAT *pa, _In_ PRAT b , uint32_t radix, int32_t precision);
extern void scale2pi( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
extern void scale( _Inout_ PRAT *px, _In_ PRAT scalefact, uint32_t radix, int32_t precision);
extern void subrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision);
extern void xorrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision);
extern void lshrat( _Inout_ PRAT *pa, _In_ PRAT b , uint32_t radix, int32_t precision);
extern void rshrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision);
extern bool rat_equ( _In_ PRAT a, _In_ PRAT b, int32_t precision);
extern bool rat_neq( _In_ PRAT a, _In_ PRAT b, int32_t precision);
extern bool rat_gt( _In_ PRAT a, _In_ PRAT b, int32_t precision);
extern bool rat_ge( _In_ PRAT a, _In_ PRAT b, int32_t precision);
extern bool rat_lt( _In_ PRAT a, _In_ PRAT b, int32_t precision);
extern bool rat_le( _In_ PRAT a, _In_ PRAT b, int32_t precision);
extern void inbetween( _In_ PRAT *px, _In_ PRAT range, int32_t precision);
extern void trimit( _Inout_ PRAT *px, int32_t precision);
extern void _dumprawrat(_In_ const wchar_t *varname, _In_ PRAT rat, std::wostream& out);
extern void _dumprawnum(_In_ const wchar_t *varname, _In_ PNUMBER num, std::wostream& out);

View File

@@ -0,0 +1,719 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//----------------------------------------------------------------------------
// Package Title ratpak
// File support.c
// Copyright (C) 1995-96 Microsoft
// Date 10-21-96
//
//
// Description
//
// Contains support functions for rationals and numbers.
//
// Special Information
//
//
//
//----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
using namespace std;
void _readconstants( void );
#if defined( GEN_CONST )
static int cbitsofprecision = 0;
#define READRAWRAT(v)
#define READRAWNUM(v)
#define DUMPRAWRAT(v) _dumprawrat(#v,v, wcout)
#define DUMPRAWNUM(v) fprintf( stderr, \
"// Autogenerated by _dumprawrat in support.c\n" ); \
fprintf( stderr, "NUMBER init_" #v "= {\n" ); \
_dumprawnum(v, wcout); \
fprintf( stderr, "};\n" )
#else
#define DUMPRAWRAT(v)
#define DUMPRAWNUM(v)
#define READRAWRAT(v) createrat(v); DUPNUM((v)->pp,(&(init_p_##v))); \
DUPNUM((v)->pq,(&(init_q_##v)));
#define READRAWNUM(v) DUPNUM(v,(&(init_##v)))
#define INIT_AND_DUMP_RAW_NUM_IF_NULL(r, v) if (r == nullptr) { r = longtonum(v, BASEX); DUMPRAWNUM(v); }
#define INIT_AND_DUMP_RAW_RAT_IF_NULL(r, v) if (r == nullptr) { r = longtorat(v); DUMPRAWRAT(v); }
static constexpr int RATIO_FOR_DECIMAL = 9;
static constexpr int DECIMAL = 10;
static constexpr int CALC_DECIMAL_DIGITS_DEFAULT = 32;
static int cbitsofprecision = RATIO_FOR_DECIMAL * DECIMAL * CALC_DECIMAL_DIGITS_DEFAULT;
#include "ratconst.h"
#endif
bool g_ftrueinfinite = false; // Set to true if you don't want
// chopping internally
// precision used internally
PNUMBER num_one= nullptr;
PNUMBER num_two= nullptr;
PNUMBER num_five= nullptr;
PNUMBER num_six= nullptr;
PNUMBER num_ten= nullptr;
PRAT ln_ten= nullptr;
PRAT ln_two= nullptr;
PRAT rat_zero= nullptr;
PRAT rat_one= nullptr;
PRAT rat_neg_one= nullptr;
PRAT rat_two= nullptr;
PRAT rat_six= nullptr;
PRAT rat_half= nullptr;
PRAT rat_ten= nullptr;
PRAT pt_eight_five= nullptr;
PRAT pi= nullptr;
PRAT pi_over_two= nullptr;
PRAT two_pi= nullptr;
PRAT one_pt_five_pi= nullptr;
PRAT e_to_one_half= nullptr;
PRAT rat_exp= nullptr;
PRAT rad_to_deg= nullptr;
PRAT rad_to_grad= nullptr;
PRAT rat_qword= nullptr;
PRAT rat_dword= nullptr; // unsigned max ulong
PRAT rat_word= nullptr;
PRAT rat_byte= nullptr;
PRAT rat_360= nullptr;
PRAT rat_400= nullptr;
PRAT rat_180= nullptr;
PRAT rat_200= nullptr;
PRAT rat_nRadix= nullptr;
PRAT rat_smallest= nullptr;
PRAT rat_negsmallest= nullptr;
PRAT rat_max_exp= nullptr;
PRAT rat_min_exp= nullptr;
PRAT rat_max_fact = nullptr;
PRAT rat_min_fact = nullptr;
PRAT rat_min_long= nullptr; // min signed long
PRAT rat_max_long= nullptr; // max signed long
//----------------------------------------------------------------------------
//
// FUNCTION: ChangeConstants
//
// ARGUMENTS: base changing to, and precision to use.
//
// RETURN: None
//
// SIDE EFFECTS: sets a mess of constants.
//
//
//----------------------------------------------------------------------------
void ChangeConstants(uint32_t radix, int32_t precision)
{
// ratio is set to the number of digits in the current radix, you can get
// in the internal BASEX radix, this is important for length calculations
// in translating from radix to BASEX and back.
uint64_t limit = static_cast<uint64_t>(BASEX) / static_cast<uint64_t>(radix);
g_ratio = 0;
for (uint32_t digit = 1; digit < limit; digit *= radix )
{
g_ratio++;
}
g_ratio += !g_ratio;
destroyrat(rat_nRadix);
rat_nRadix=longtorat( radix );
// Check to see what we have to recalculate and what we don't
if (cbitsofprecision < (g_ratio * static_cast<int32_t>(radix) * precision))
{
g_ftrueinfinite = false;
INIT_AND_DUMP_RAW_NUM_IF_NULL(num_one, 1L);
INIT_AND_DUMP_RAW_NUM_IF_NULL(num_two, 2L);
INIT_AND_DUMP_RAW_NUM_IF_NULL(num_five, 5L);
INIT_AND_DUMP_RAW_NUM_IF_NULL(num_six, 6L);
INIT_AND_DUMP_RAW_NUM_IF_NULL(num_ten, 10L);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_six, 6L);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_two, 2L);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_zero, 0L);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_one, 1L);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_neg_one, -1L);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_ten, 10L);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_word, 0xffff);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_word, 0xff);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_400, 400);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_360, 360);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_200, 200);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_180, 180);
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_max_exp, 100000);
// 3248, is the max number for which calc is able to compute factorial, after that it is unable to compute due to overflow.
// Hence restricted factorial range as at most 3248.Beyond that calc will throw overflow error immediately.
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_max_fact, 3249);
// -1000, is the min number for which calc is able to compute factorial, after that it takes too long to compute.
INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_min_fact, -1000);
DUPRAT(rat_smallest, rat_nRadix);
ratpowlong(&rat_smallest, -precision, precision);
DUPRAT(rat_negsmallest, rat_smallest);
rat_negsmallest->pp->sign = -1;
DUMPRAWRAT(rat_smallest);
DUMPRAWRAT(rat_negsmallest);
if (rat_half == nullptr)
{
createrat(rat_half);
DUPNUM(rat_half->pp, num_one);
DUPNUM(rat_half->pq, num_two);
DUMPRAWRAT(rat_half);
}
if (pt_eight_five == nullptr)
{
createrat(pt_eight_five);
pt_eight_five->pp = longtonum(85L, BASEX);
pt_eight_five->pq = longtonum(100L, BASEX);
DUMPRAWRAT(pt_eight_five);
}
DUPRAT(rat_qword, rat_two);
numpowlong(&(rat_qword->pp), 64, BASEX, precision);
subrat(&rat_qword, rat_one, precision);
DUMPRAWRAT(rat_qword);
DUPRAT(rat_dword, rat_two);
numpowlong(&(rat_dword->pp), 32, BASEX, precision);
subrat(&rat_dword, rat_one, precision);
DUMPRAWRAT(rat_dword);
DUPRAT(rat_max_long, rat_two);
numpowlong(&(rat_max_long->pp), 31, BASEX, precision);
DUPRAT(rat_min_long, rat_max_long);
subrat(&rat_max_long, rat_one, precision); // rat_max_long = 2^31 -1
DUMPRAWRAT(rat_max_long);
rat_min_long->pp->sign *= -1; // rat_min_long = -2^31
DUMPRAWRAT(rat_min_long);
DUPRAT(rat_min_exp, rat_max_exp);
rat_min_exp->pp->sign *= -1;
DUMPRAWRAT(rat_min_exp);
cbitsofprecision = g_ratio * radix * precision;
// Apparently when dividing 180 by pi, another (internal) digit of
// precision is needed.
long extraPrecision = precision + g_ratio;
DUPRAT(pi, rat_half);
asinrat(&pi, radix, extraPrecision);
mulrat(&pi, rat_six, extraPrecision);
DUMPRAWRAT(pi);
DUPRAT(two_pi, pi);
DUPRAT(pi_over_two, pi);
DUPRAT(one_pt_five_pi, pi);
addrat(&two_pi, pi, extraPrecision);
DUMPRAWRAT(two_pi);
divrat(&pi_over_two, rat_two, extraPrecision);
DUMPRAWRAT(pi_over_two);
addrat(&one_pt_five_pi, pi_over_two, extraPrecision);
DUMPRAWRAT(one_pt_five_pi);
DUPRAT(e_to_one_half, rat_half);
_exprat(&e_to_one_half, extraPrecision);
DUMPRAWRAT(e_to_one_half);
DUPRAT(rat_exp, rat_one);
_exprat(&rat_exp, extraPrecision);
DUMPRAWRAT(rat_exp);
// WARNING: remember lograt uses exponent constants calculated above...
DUPRAT(ln_ten, rat_ten);
lograt(&ln_ten, extraPrecision);
DUMPRAWRAT(ln_ten);
DUPRAT(ln_two, rat_two);
lograt(&ln_two, extraPrecision);
DUMPRAWRAT(ln_two);
destroyrat(rad_to_deg);
rad_to_deg = longtorat(180L);
divrat(&rad_to_deg, pi, extraPrecision);
DUMPRAWRAT(rad_to_deg);
destroyrat(rad_to_grad);
rad_to_grad = longtorat(200L);
divrat(&rad_to_grad, pi, extraPrecision);
DUMPRAWRAT(rad_to_grad);
}
else
{
_readconstants();
DUPRAT(rat_smallest, rat_nRadix);
ratpowlong(&rat_smallest, -precision, precision);
DUPRAT(rat_negsmallest, rat_smallest);
rat_negsmallest->pp->sign = -1;
}
}
//----------------------------------------------------------------------------
//
// FUNCTION: intrat
//
// ARGUMENTS: pointer to x PRAT representation of number
//
// RETURN: no return value x PRAT is smashed with integral number
//
//
//----------------------------------------------------------------------------
void intrat( PRAT *px, uint32_t radix, int32_t precision)
{
// Only do the intrat operation if number is nonzero.
// and only if the bottom part is not one.
if ( !zernum( (*px)->pp ) && !equnum( (*px)->pq, num_one ) )
{
wstring ratStr = RatToString(*px, FMT_FLOAT, radix, precision);
PNUMBER pnum = StringToNumber(ratStr, radix, precision);
destroyrat( *px );
*px = numtorat( pnum, radix);
destroynum( pnum );
PRAT pret = nullptr;
DUPRAT(pret,*px);
modrat( &pret, rat_one );
subrat( px, pret, precision);
destroyrat( pret );
}
}
//---------------------------------------------------------------------------
//
// FUNCTION: rat_equ
//
// ARGUMENTS: PRAT a and PRAT b
//
// RETURN: true if equal false otherwise.
//
//
//---------------------------------------------------------------------------
bool rat_equ( PRAT a, PRAT b, int32_t precision)
{
PRAT rattmp= nullptr;
DUPRAT(rattmp,a);
rattmp->pp->sign *= -1;
addrat( &rattmp, b, precision);
bool bret = zernum( rattmp->pp );
destroyrat( rattmp );
return( bret );
}
//---------------------------------------------------------------------------
//
// FUNCTION: rat_ge
//
// ARGUMENTS: PRAT a, PRAT b and long precision
//
// RETURN: true if a is greater than or equal to b
//
//
//---------------------------------------------------------------------------
bool rat_ge( PRAT a, PRAT b, int32_t precision)
{
PRAT rattmp= nullptr;
DUPRAT(rattmp,a);
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
bool bret = ( zernum( rattmp->pp ) ||
rattmp->pp->sign * rattmp->pq->sign == 1 );
destroyrat( rattmp );
return( bret );
}
//---------------------------------------------------------------------------
//
// FUNCTION: rat_gt
//
// ARGUMENTS: PRAT a and PRAT b
//
// RETURN: true if a is greater than b
//
//
//---------------------------------------------------------------------------
bool rat_gt( PRAT a, PRAT b, int32_t precision)
{
PRAT rattmp= nullptr;
DUPRAT(rattmp,a);
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
bool bret = ( !zernum( rattmp->pp ) &&
rattmp->pp->sign * rattmp->pq->sign == 1 );
destroyrat( rattmp );
return( bret );
}
//---------------------------------------------------------------------------
//
// FUNCTION: rat_le
//
// ARGUMENTS: PRAT a, PRAT b and long precision
//
// RETURN: true if a is less than or equal to b
//
//
//---------------------------------------------------------------------------
bool rat_le( PRAT a, PRAT b, int32_t precision)
{
PRAT rattmp= nullptr;
DUPRAT(rattmp,a);
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
bool bret = ( zernum( rattmp->pp ) ||
rattmp->pp->sign * rattmp->pq->sign == -1 );
destroyrat( rattmp );
return( bret );
}
//---------------------------------------------------------------------------
//
// FUNCTION: rat_lt
//
// ARGUMENTS: PRAT a, PRAT b and long precision
//
// RETURN: true if a is less than b
//
//
//---------------------------------------------------------------------------
bool rat_lt( PRAT a, PRAT b, int32_t precision)
{
PRAT rattmp= nullptr;
DUPRAT(rattmp,a);
b->pp->sign *= -1;
addrat( &rattmp, b, precision);
b->pp->sign *= -1;
bool bret = ( !zernum( rattmp->pp ) &&
rattmp->pp->sign * rattmp->pq->sign == -1 );
destroyrat( rattmp );
return( bret );
}
//---------------------------------------------------------------------------
//
// FUNCTION: rat_neq
//
// ARGUMENTS: PRAT a and PRAT b
//
// RETURN: true if a is not equal to b
//
//
//---------------------------------------------------------------------------
bool rat_neq( PRAT a, PRAT b, int32_t precision)
{
PRAT rattmp= nullptr;
DUPRAT(rattmp,a);
rattmp->pp->sign *= -1;
addrat( &rattmp, b, precision);
bool bret = !( zernum( rattmp->pp ) );
destroyrat( rattmp );
return( bret );
}
//---------------------------------------------------------------------------
//
// function: scale
//
// ARGUMENTS: pointer to x PRAT representation of number, and scaling factor
//
// RETURN: no return, value x PRAT is smashed with a scaled number in the
// range of the scalefact.
//
//---------------------------------------------------------------------------
void scale( PRAT *px, PRAT scalefact, uint32_t radix, int32_t precision )
{
PRAT pret = nullptr;
DUPRAT(pret,*px);
// Logscale is a quick way to tell how much extra precision is needed for
// scaleing by scalefact.
long logscale = g_ratio * ( (pret->pp->cdigit+pret->pp->exp) -
(pret->pq->cdigit+pret->pq->exp) );
if ( logscale > 0 )
{
precision += logscale;
}
divrat( &pret, scalefact, precision);
intrat(&pret, radix, precision);
mulrat( &pret, scalefact, precision);
pret->pp->sign *= -1;
addrat( px, pret, precision);
destroyrat( pret );
}
//---------------------------------------------------------------------------
//
// function: scale2pi
//
// ARGUMENTS: pointer to x PRAT representation of number
//
// RETURN: no return, value x PRAT is smashed with a scaled number in the
// range of 0..2pi
//
//---------------------------------------------------------------------------
void scale2pi( PRAT *px, uint32_t radix, int32_t precision )
{
PRAT pret = nullptr;
PRAT my_two_pi = nullptr;
DUPRAT(pret,*px);
// Logscale is a quick way to tell how much extra precision is needed for
// scaleing by 2 pi.
long logscale = g_ratio * ( (pret->pp->cdigit+pret->pp->exp) -
(pret->pq->cdigit+pret->pq->exp) );
if ( logscale > 0 )
{
precision += logscale;
DUPRAT(my_two_pi,rat_half);
asinrat( &my_two_pi, radix, precision);
mulrat( &my_two_pi, rat_six, precision);
mulrat( &my_two_pi, rat_two, precision);
}
else
{
DUPRAT(my_two_pi,two_pi);
logscale = 0;
}
divrat( &pret, my_two_pi, precision);
intrat(&pret, radix, precision);
mulrat( &pret, my_two_pi, precision);
pret->pp->sign *= -1;
addrat( px, pret, precision);
destroyrat( my_two_pi );
destroyrat( pret );
}
//---------------------------------------------------------------------------
//
// FUNCTION: inbetween
//
// ARGUMENTS: PRAT *px, and PRAT range.
//
// RETURN: none, changes *px to -/+range, if px is outside -range..+range
//
//---------------------------------------------------------------------------
void inbetween( PRAT *px, PRAT range, int32_t precision)
{
if ( rat_gt(*px,range, precision) )
{
DUPRAT(*px,range);
}
else
{
range->pp->sign *= -1;
if ( rat_lt(*px, range, precision) )
{
DUPRAT(*px,range);
}
range->pp->sign *= -1;
}
}
//---------------------------------------------------------------------------
//
// FUNCTION: _dumprawrat
//
// ARGUMENTS: const wchar *name of variable, PRAT x, output stream out
//
// RETURN: none, prints the results of a dump of the internal structures
// of a PRAT, suitable for READRAWRAT to stderr.
//
//---------------------------------------------------------------------------
void _dumprawrat( const wchar_t *varname, PRAT rat, wostream& out)
{
_dumprawnum(varname, rat->pp, out );
_dumprawnum(varname, rat->pq, out );
}
//---------------------------------------------------------------------------
//
// FUNCTION: _dumprawnum
//
// ARGUMENTS: const wchar *name of variable, PNUMBER num, output stream out
//
// RETURN: none, prints the results of a dump of the internal structures
// of a PNUMBER, suitable for READRAWNUM to stderr.
//
//---------------------------------------------------------------------------
void _dumprawnum(const wchar_t *varname, PNUMBER num, wostream& out)
{
int i;
out << L"NUMBER " << varname << L" = {\n";
out << L"\t"<< num->sign << L",\n";
out << L"\t" << num->cdigit << L",\n";
out << L"\t" << num->exp << L",\n";
out << L"\t{ ";
for ( i = 0; i < num->cdigit; i++ )
{
out << L" "<< num->mant[i] << L",";
}
out << L"}\n";
out << L"};\n";
}
void _readconstants( void )
{
READRAWNUM(num_one);
READRAWNUM(num_two);
READRAWNUM(num_five);
READRAWNUM(num_six);
READRAWNUM(num_ten);
READRAWRAT(pt_eight_five);
READRAWRAT(rat_six);
READRAWRAT(rat_two);
READRAWRAT(rat_zero);
READRAWRAT(rat_one);
READRAWRAT(rat_neg_one);
READRAWRAT(rat_half);
READRAWRAT(rat_ten);
READRAWRAT(pi);
READRAWRAT(two_pi);
READRAWRAT(pi_over_two);
READRAWRAT(one_pt_five_pi);
READRAWRAT(e_to_one_half);
READRAWRAT(rat_exp);
READRAWRAT(ln_ten);
READRAWRAT(ln_two);
READRAWRAT(rad_to_deg);
READRAWRAT(rad_to_grad);
READRAWRAT(rat_qword);
READRAWRAT(rat_dword);
READRAWRAT(rat_word);
READRAWRAT(rat_byte);
READRAWRAT(rat_360);
READRAWRAT(rat_400);
READRAWRAT(rat_180);
READRAWRAT(rat_200);
READRAWRAT(rat_smallest);
READRAWRAT(rat_negsmallest);
READRAWRAT(rat_max_exp);
READRAWRAT(rat_min_exp);
READRAWRAT(rat_max_fact);
READRAWRAT(rat_min_fact);
READRAWRAT(rat_min_long);
READRAWRAT(rat_max_long);
}
//---------------------------------------------------------------------------
//
// FUNCTION: trimit
//
// ARGUMENTS: PRAT *px, long precision
//
//
// DESCRIPTION: Chops off digits from rational numbers to avoid time
// explosions in calculations of functions using series.
// It can be shown that it is enough to only keep the first n digits
// of the largest of p or q in the rational p over q form, and of course
// scale the smaller by the same number of digits. This will give you
// n-1 digits of accuracy. This dramatically speeds up calculations
// involving hundreds of digits or more.
// The last part of this trim dealing with exponents never affects accuracy
//
// RETURN: none, modifies the pointed to PRAT
//
//---------------------------------------------------------------------------
void trimit( PRAT *px, int32_t precision)
{
if ( !g_ftrueinfinite )
{
long trim;
PNUMBER pp=(*px)->pp;
PNUMBER pq=(*px)->pq;
trim = g_ratio * (min((pp->cdigit+pp->exp),(pq->cdigit+pq->exp))-1) - precision;
if ( trim > g_ratio )
{
trim /= g_ratio;
if ( trim <= pp->exp )
{
pp->exp -= trim;
}
else
{
memmove( pp->mant, &(pp->mant[trim-pp->exp]), sizeof(MANTTYPE)*(pp->cdigit-trim+pp->exp) );
pp->cdigit -= trim-pp->exp;
pp->exp = 0;
}
if ( trim <= pq->exp )
{
pq->exp -= trim;
}
else
{
memmove( pq->mant, &(pq->mant[trim-pq->exp]), sizeof(MANTTYPE)*(pq->cdigit-trim+pq->exp) );
pq->cdigit -= trim-pq->exp;
pq->exp = 0;
}
}
trim = min(pp->exp,pq->exp);
pp->exp -= trim;
pq->exp -= trim;
}
}

View File

@@ -0,0 +1,297 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//----------------------------------------------------------------------------
// File trans.c
// Copyright (C) 1995-96 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains sin, cos and tan for rationals
//
//
//----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
void scalerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision )
{
switch ( angletype )
{
case ANGLE_RAD:
scale2pi( pa, radix, precision);
break;
case ANGLE_DEG:
scale( pa, rat_360, radix, precision);
break;
case ANGLE_GRAD:
scale( pa, rat_400, radix, precision);
break;
}
}
//-----------------------------------------------------------------------------
//
// FUNCTION: sinrat, _sinrat
//
// ARGUMENTS: x PRAT representation of number to take the sine of
//
// RETURN: sin of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2j+1
// \ ] j X
// \ -1 * ---------
// / (2j+1)!
// /__]
// j=0
// or,
// n
// ___ 2
// \ ] -X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j)*(2j+1)
// /__]
// j=0
//
// thisterm = X ; and stop when thisterm < precision used.
// 0 n
//
//-----------------------------------------------------------------------------
void _sinrat( PRAT *px, int32_t precision)
{
CREATETAYLOR();
DUPRAT(pret,*px);
DUPRAT(thisterm,*px);
DUPNUM(n2,num_one);
xx->pp->sign *= -1;
do {
NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision);
} while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
// Since *px might be epsilon above 1 or below -1, due to TRIMIT we need
// this trick here.
inbetween(px, rat_one, precision);
// Since *px might be epsilon near zero we must set it to zero.
if ( rat_le(*px, rat_smallest, precision) && rat_ge(*px, rat_negsmallest, precision) )
{
DUPRAT(*px,rat_zero);
}
}
void sinrat( PRAT *px, uint32_t radix, int32_t precision)
{
scale2pi(px, radix, precision);
_sinrat(px, precision);
}
void sinanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
scalerat( pa, angletype, radix, precision);
switch ( angletype )
{
case ANGLE_DEG:
if ( rat_gt( *pa, rat_180, precision) )
{
subrat(pa, rat_360, precision);
}
divrat( pa, rat_180, precision);
mulrat( pa, pi, precision);
break;
case ANGLE_GRAD:
if ( rat_gt( *pa, rat_200, precision) )
{
subrat(pa,rat_400, precision);
}
divrat( pa, rat_200, precision);
mulrat( pa, pi, precision);
break;
}
_sinrat( pa, precision);
}
//-----------------------------------------------------------------------------
//
// FUNCTION: cosrat, _cosrat
//
// ARGUMENTS: x PRAT representation of number to take the cosine of
//
// RETURN: cosin of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2j j
// \ ] X -1
// \ ---------
// / (2j)!
// /__]
// j=0
// or,
// n
// ___ 2
// \ ] -X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j)*(2j+1)
// /__]
// j=0
//
// thisterm = 1 ; and stop when thisterm < precision used.
// 0 n
//
//-----------------------------------------------------------------------------
void _cosrat( PRAT *px, uint32_t radix, int32_t precision)
{
CREATETAYLOR();
destroynum(pret->pp);
destroynum(pret->pq);
pret->pp=longtonum( 1L, radix);
pret->pq=longtonum( 1L, radix);
DUPRAT(thisterm,pret)
n2=longtonum(0L, radix);
xx->pp->sign *= -1;
do {
NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision);
} while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
// Since *px might be epsilon above 1 or below -1, due to TRIMIT we need
// this trick here.
inbetween(px, rat_one, precision);
// Since *px might be epsilon near zero we must set it to zero.
if ( rat_le(*px, rat_smallest, precision) && rat_ge(*px, rat_negsmallest, precision) )
{
DUPRAT(*px,rat_zero);
}
}
void cosrat( PRAT *px, uint32_t radix, int32_t precision)
{
scale2pi(px, radix, precision);
_cosrat(px, radix, precision);
}
void cosanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
scalerat( pa, angletype, radix, precision);
switch ( angletype )
{
case ANGLE_DEG:
if ( rat_gt( *pa, rat_180, precision) )
{
PRAT ptmp= nullptr;
DUPRAT(ptmp,rat_360);
subrat(&ptmp, *pa, precision);
destroyrat(*pa);
*pa=ptmp;
}
divrat( pa, rat_180, precision);
mulrat( pa, pi, precision);
break;
case ANGLE_GRAD:
if ( rat_gt( *pa, rat_200, precision) )
{
PRAT ptmp= nullptr;
DUPRAT(ptmp,rat_400);
subrat(&ptmp, *pa, precision);
destroyrat(*pa);
*pa=ptmp;
}
divrat( pa, rat_200, precision);
mulrat( pa, pi, precision);
break;
}
_cosrat( pa, radix, precision);
}
//-----------------------------------------------------------------------------
//
// FUNCTION: tanrat, _tanrat
//
// ARGUMENTS: x PRAT representation of number to take the tangent of
//
// RETURN: tan of x in PRAT form.
//
// EXPLANATION: This uses sinrat and cosrat
//
//-----------------------------------------------------------------------------
void _tanrat( PRAT *px, uint32_t radix, int32_t precision)
{
PRAT ptmp= nullptr;
DUPRAT(ptmp,*px);
_sinrat(px, precision);
_cosrat(&ptmp, radix, precision);
if ( zerrat( ptmp ) )
{
destroyrat(ptmp);
throw( CALC_E_DOMAIN );
}
divrat(px, ptmp, precision);
destroyrat(ptmp);
}
void tanrat( PRAT *px, uint32_t radix, int32_t precision)
{
scale2pi(px, radix, precision);
_tanrat(px, radix, precision);
}
void tananglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
{
scalerat( pa, angletype, radix, precision);
switch ( angletype )
{
case ANGLE_DEG:
if ( rat_gt( *pa, rat_180, precision) )
{
subrat(pa, rat_180, precision);
}
divrat( pa, rat_180, precision);
mulrat( pa, pi, precision);
break;
case ANGLE_GRAD:
if ( rat_gt( *pa, rat_200, precision) )
{
subrat(pa, rat_200, precision);
}
divrat( pa, rat_200, precision);
mulrat( pa, pi, precision);
break;
}
_tanrat( pa, radix, precision);
}

View File

@@ -0,0 +1,231 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//-----------------------------------------------------------------------------
// Package Title ratpak
// File transh.c
// Copyright (C) 1995-96 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains hyperbolic sin, cos, and tan for rationals.
//
//
//-----------------------------------------------------------------------------
#include "pch.h"
#include "ratpak.h"
bool IsValidForHypFunc(PRAT px, int32_t precision)
{
PRAT ptmp = nullptr;
bool bRet = true;
DUPRAT(ptmp,rat_min_exp);
divrat(&ptmp, rat_ten, precision);
if ( rat_lt( px, ptmp, precision) )
{
bRet = false;
}
destroyrat( ptmp );
return bRet;
}
//-----------------------------------------------------------------------------
//
// FUNCTION: sinhrat, _sinhrat
//
// ARGUMENTS: x PRAT representation of number to take the sine hyperbolic
// of
// RETURN: sinh of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2j+1
// \ ] X
// \ ---------
// / (2j+1)!
// /__]
// j=0
// or,
// n
// ___ 2
// \ ] X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j)*(2j+1)
// /__]
// j=0
//
// thisterm = X ; and stop when thisterm < precision used.
// 0 n
//
// if x is bigger than 1.0 (e^x-e^-x)/2 is used.
//
//-----------------------------------------------------------------------------
void _sinhrat( PRAT *px, int32_t precision)
{
if ( !IsValidForHypFunc(*px, precision))
{
// Don't attempt exp of anything large or small
throw( CALC_E_DOMAIN );
}
CREATETAYLOR();
DUPRAT(pret,*px);
DUPRAT(thisterm,pret);
DUPNUM(n2,num_one);
do {
NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision);
} while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
void sinhrat( PRAT *px, uint32_t radix, int32_t precision)
{
PRAT tmpx= nullptr;
if ( rat_ge( *px, rat_one, precision) )
{
DUPRAT(tmpx,*px);
exprat(px, radix, precision);
tmpx->pp->sign *= -1;
exprat(&tmpx, radix, precision);
subrat( px, tmpx, precision);
divrat( px, rat_two, precision);
destroyrat( tmpx );
}
else
{
_sinhrat( px, precision);
}
}
//-----------------------------------------------------------------------------
//
// FUNCTION: coshrat
//
// ARGUMENTS: x PRAT representation of number to take the cosine
// hyperbolic of
//
// RETURN: cosh of x in PRAT form.
//
// EXPLANATION: This uses Taylor series
//
// n
// ___ 2j
// \ ] X
// \ ---------
// / (2j)!
// /__]
// j=0
// or,
// n
// ___ 2
// \ ] X
// \ thisterm ; where thisterm = thisterm * ---------
// / j j+1 j (2j)*(2j+1)
// /__]
// j=0
//
// thisterm = 1 ; and stop when thisterm < precision used.
// 0 n
//
// if x is bigger than 1.0 (e^x+e^-x)/2 is used.
//
//-----------------------------------------------------------------------------
void _coshrat( PRAT *px, uint32_t radix, int32_t precision)
{
if ( !IsValidForHypFunc(*px, precision))
{
// Don't attempt exp of anything large or small
throw( CALC_E_DOMAIN );
}
CREATETAYLOR();
pret->pp=longtonum( 1L, radix);
pret->pq=longtonum( 1L, radix);
DUPRAT(thisterm,pret)
n2=longtonum(0L, radix);
do {
NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision);
} while ( !SMALL_ENOUGH_RAT( thisterm, precision) );
DESTROYTAYLOR();
}
void coshrat( PRAT *px, uint32_t radix, int32_t precision)
{
PRAT tmpx= nullptr;
(*px)->pp->sign = 1;
(*px)->pq->sign = 1;
if ( rat_ge( *px, rat_one, precision) )
{
DUPRAT(tmpx,*px);
exprat(px, radix, precision);
tmpx->pp->sign *= -1;
exprat(&tmpx, radix, precision);
addrat( px, tmpx, precision);
divrat( px, rat_two, precision);
destroyrat( tmpx );
}
else
{
_coshrat( px, radix, precision);
}
// Since *px might be epsilon below 1 due to TRIMIT
// we need this trick here.
if ( rat_lt(*px, rat_one, precision) )
{
DUPRAT(*px,rat_one);
}
}
//-----------------------------------------------------------------------------
//
// FUNCTION: tanhrat
//
// ARGUMENTS: x PRAT representation of number to take the tangent
// hyperbolic of
//
// RETURN: tanh of x in PRAT form.
//
// EXPLANATION: This uses sinhrat and coshrat
//
//-----------------------------------------------------------------------------
void tanhrat( PRAT *px, uint32_t radix, int32_t precision)
{
PRAT ptmp= nullptr;
DUPRAT(ptmp,*px);
sinhrat(px, radix, precision);
coshrat(&ptmp, radix, precision);
mulnumx(&((*px)->pp),ptmp->pq);
mulnumx(&((*px)->pq),ptmp->pp);
destroyrat(ptmp);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,266 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
namespace UnitConversionManager
{
enum class Command;
struct Unit
{
Unit(){}
Unit(int id, std::wstring name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical)
: id(id), name(name), accessibleName(name), abbreviation(abbreviation), isConversionSource(isConversionSource), isConversionTarget(isConversionTarget), isWhimsical(isWhimsical)
{
}
Unit(int id, std::wstring currencyName, std::wstring countryName, std::wstring abbreviation, bool isRtlLanguage, bool isConversionSource, bool isConversionTarget)
: id(id), abbreviation(abbreviation), isConversionSource(isConversionSource), isConversionTarget(isConversionTarget), isWhimsical(false)
{
std::wstring nameValue1 = isRtlLanguage ? currencyName : countryName;
std::wstring nameValue2 = isRtlLanguage ? countryName : currencyName;
name = nameValue1 + L" - " + nameValue2;
accessibleName = nameValue1 + L" " + nameValue2;
}
virtual ~Unit() {}
int id;
std::wstring name;
std::wstring accessibleName;
std::wstring abbreviation;
bool isConversionSource;
bool isConversionTarget;
bool isWhimsical;
bool operator!= (const Unit& that) const
{
return that.id != id;
}
bool operator== (const Unit& that) const
{
return that.id == id;
}
};
// The EMPTY_UNIT acts as a 'null-struct' so that
// Unit pointers can safely be dereferenced without
// null checks.
//
// unitId, name, abbreviation, isConversionSource, isConversionTarget, isWhimsical
const Unit EMPTY_UNIT = Unit{ -1, L"", L"", true, true, false };
struct Category
{
Category(){}
Category(int id, std::wstring name, bool supportsNegative) : id(id), name(name), supportsNegative(supportsNegative)
{
}
int id;
std::wstring name;
bool supportsNegative;
bool operator!= (const Category& that) const
{
return that.id != id;
}
bool operator== (const Category& that) const
{
return that.id == id;
}
};
class UnitHash
{
public:
size_t operator() (const Unit & x) const {
return x.id;
}
};
class CategoryHash
{
public:
size_t operator() (const Category & x) const {
return x.id;
}
};
struct SuggestedValueIntermediate
{
double magnitude;
double value;
Unit type;
};
struct ConversionData
{
ConversionData(){}
ConversionData(double ratio, double offset, bool offsetFirst) : ratio(ratio), offset(offset), offsetFirst(offsetFirst)
{
}
virtual ~ConversionData() {}
double ratio;
double offset;
bool offsetFirst;
};
struct CurrencyStaticData
{
std::wstring countryCode;
std::wstring countryName;
std::wstring currencyCode;
std::wstring currencyName;
std::wstring currencySymbol;
};
struct CurrencyRatio
{
double ratio;
std::wstring sourceCurrencyCode;
std::wstring targetCurrencyCode;
};
typedef std::tuple<std::vector<UnitConversionManager::Unit>, UnitConversionManager::Unit, UnitConversionManager::Unit> CategorySelectionInitializer;
typedef std::unordered_map<UnitConversionManager::Unit, std::unordered_map<UnitConversionManager::Unit, UnitConversionManager::ConversionData, UnitConversionManager::UnitHash>, UnitConversionManager::UnitHash> UnitToUnitToConversionDataMap;
typedef std::unordered_map<UnitConversionManager::Category, std::vector<UnitConversionManager::Unit>, UnitConversionManager::CategoryHash> CategoryToUnitVectorMap;
class IViewModelCurrencyCallback
{
public:
virtual ~IViewModelCurrencyCallback() { };
virtual void CurrencyDataLoadFinished(bool didLoad) = 0;
virtual void CurrencySymbolsCallback(_In_ const std::wstring& fromSymbol, _In_ const std::wstring& toSymbol) = 0;
virtual void CurrencyRatiosCallback(_In_ const std::wstring& ratioEquality, _In_ const std::wstring& accRatioEquality) = 0;
virtual void CurrencyTimestampCallback(_In_ const std::wstring& timestamp, bool isWeekOldData) = 0;
virtual void NetworkBehaviorChanged(_In_ int newBehavior) = 0;
};
class IConverterDataLoader
{
public:
virtual ~IConverterDataLoader() { };
virtual void LoadData() = 0; // prepare data if necessary before calling other functions
virtual std::vector<Category> LoadOrderedCategories() = 0;
virtual std::vector<Unit> LoadOrderedUnits(const Category& c) = 0;
virtual std::unordered_map<Unit, ConversionData, UnitHash> LoadOrderedRatios(const Unit& u) = 0;
virtual bool SupportsCategory(const Category& target) = 0;
};
class ICurrencyConverterDataLoader
{
public:
virtual void SetViewModelCallback(const std::shared_ptr<UnitConversionManager::IViewModelCurrencyCallback>& callback) = 0;
virtual std::pair<std::wstring, std::wstring> GetCurrencySymbols(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0;
virtual std::pair<std::wstring, std::wstring> GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0;
virtual std::wstring GetCurrencyTimestamp() = 0;
virtual concurrency::task<bool> TryLoadDataFromCacheAsync() = 0;
virtual concurrency::task<bool> TryLoadDataFromWebAsync() = 0;
virtual concurrency::task<bool> TryLoadDataFromWebOverrideAsync() = 0;
};
class IUnitConverterVMCallback
{
public:
virtual ~IUnitConverterVMCallback() { };
virtual void DisplayCallback(const std::wstring& from, const std::wstring& to) = 0;
virtual void SuggestedValueCallback(const std::vector<std::tuple<std::wstring, Unit>>& suggestedValues) = 0;
virtual void MaxDigitsReached() = 0;
};
class IUnitConverter
{
public:
virtual ~IUnitConverter() { }
virtual void Initialize() = 0; // Use to initialize first time, use deserialize instead to rehydrate
virtual std::vector<Category> GetCategories() = 0;
virtual CategorySelectionInitializer SetCurrentCategory(const Category& input) = 0;
virtual Category GetCurrentCategory() = 0;
virtual void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) = 0;
virtual void SwitchActive(const std::wstring& newValue) = 0;
virtual std::wstring Serialize() = 0;
virtual void DeSerialize(const std::wstring& serializedData) = 0;
virtual std::wstring SaveUserPreferences() = 0;
virtual void RestoreUserPreferences(_In_ const std::wstring& userPreferences) = 0;
virtual void SendCommand(Command command) = 0;
virtual void SetViewModelCallback(_In_ const std::shared_ptr<IUnitConverterVMCallback>& newCallback) = 0;
virtual void SetViewModelCurrencyCallback(_In_ const std::shared_ptr<IViewModelCurrencyCallback>& newCallback) = 0;
virtual concurrency::task<std::pair<bool, std::wstring>> RefreshCurrencyRatios() = 0;
};
class UnitConverter : public IUnitConverter, public std::enable_shared_from_this<UnitConverter>
{
public:
UnitConverter(_In_ const std::shared_ptr<IConverterDataLoader>& dataLoader);
UnitConverter(_In_ const std::shared_ptr<IConverterDataLoader>& dataLoader, _In_ const std::shared_ptr<IConverterDataLoader>& currencyDataLoader);
// IUnitConverter
void Initialize() override;
std::vector<Category> GetCategories() override;
CategorySelectionInitializer SetCurrentCategory(const Category& input) override;
Category GetCurrentCategory() override;
void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) override;
void SwitchActive(const std::wstring& newValue) override;
std::wstring Serialize() override;
void DeSerialize(const std::wstring& serializedData) override;
std::wstring SaveUserPreferences() override;
void RestoreUserPreferences(const std::wstring& userPreference) override;
void SendCommand(Command command) override;
void SetViewModelCallback(_In_ const std::shared_ptr<IUnitConverterVMCallback>& newCallback) override;
void SetViewModelCurrencyCallback(_In_ const std::shared_ptr<IViewModelCurrencyCallback>& newCallback) override;
concurrency::task<std::pair<bool, std::wstring>> RefreshCurrencyRatios() override;
// IUnitConverter
static std::vector<std::wstring> StringToVector(const std::wstring& w, const wchar_t * delimiter, bool addRemainder = false);
static std::wstring Quote(const std::wstring& s);
static std::wstring Unquote(const std::wstring& s);
private:
bool CheckLoad();
double Convert(double value, ConversionData conversionData);
std::vector<std::tuple<std::wstring, Unit>> CalculateSuggested();
void Reset();
void ClearValues();
void Calculate();
void TrimString(std::wstring& input);
void InitializeSelectedUnits();
std::wstring RoundSignificant(double num, int numSignificant);
Category StringToCategory(const std::wstring& w);
std::wstring CategoryToString(const Category& c, const wchar_t * delimiter);
std::wstring UnitToString(const Unit& u, const wchar_t * delimiter);
Unit StringToUnit(const std::wstring& w);
ConversionData StringToConversionData(const std::wstring& w);
std::wstring ConversionDataToString(ConversionData d, const wchar_t * delimiter);
void UpdateCurrencySymbols();
void UpdateViewModel();
bool AnyUnitIsEmpty();
std::shared_ptr<IConverterDataLoader> GetDataLoaderForCategory(const Category& category);
std::shared_ptr<ICurrencyConverterDataLoader> GetCurrencyConverterDataLoader();
private:
std::shared_ptr<IConverterDataLoader> m_dataLoader;
std::shared_ptr<IConverterDataLoader> m_currencyDataLoader;
std::shared_ptr<IUnitConverterVMCallback> m_vmCallback;
std::shared_ptr<IViewModelCurrencyCallback> m_vmCurrencyCallback;
std::vector<Category> m_categories;
CategoryToUnitVectorMap m_categoryToUnits;
UnitToUnitToConversionDataMap m_ratioMap;
Category m_currentCategory;
Unit m_fromType;
Unit m_toType;
std::wstring m_currentDisplay;
std::wstring m_returnDisplay;
bool m_currentHasDecimal;
bool m_returnHasDecimal;
bool m_switchedActive;
};
}

4
src/CalcManager/pch.cpp Normal file
View File

@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"

23
src/CalcManager/pch.h Normal file
View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <assert.h>
#include <windows.h>
#include <winerror.h>
#include <sstream>
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <limits>
#include <regex>
#include <unordered_map>
#include <intsafe.h>
#include <array>
#include <ppltasks.h>

View File

@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>