Hello GitHub
This commit is contained in:
317
src/CalcManager/CEngine/CalcInput.cpp
Normal file
317
src/CalcManager/CEngine/CalcInput.cpp
Normal 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;
|
||||
}
|
||||
58
src/CalcManager/CEngine/CalcUtils.cpp
Normal file
58
src/CalcManager/CEngine/CalcUtils.cpp
Normal 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;
|
||||
}
|
||||
490
src/CalcManager/CEngine/History.cpp
Normal file
490
src/CalcManager/CEngine/History.cpp
Normal 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, ¤tPair));
|
||||
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;
|
||||
}
|
||||
64
src/CalcManager/CEngine/Number.cpp
Normal file
64
src/CalcManager/CEngine/Number.cpp
Normal 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; });
|
||||
}
|
||||
}
|
||||
61
src/CalcManager/CEngine/Rational.cpp
Normal file
61
src/CalcManager/CEngine/Rational.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
220
src/CalcManager/CEngine/calc.cpp
Normal file
220
src/CalcManager/CEngine/calc.cpp
Normal 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;
|
||||
}
|
||||
1107
src/CalcManager/CEngine/scicomm.cpp
Normal file
1107
src/CalcManager/CEngine/scicomm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
400
src/CalcManager/CEngine/scidisp.cpp
Normal file
400
src/CalcManager/CEngine/scidisp.cpp
Normal 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;
|
||||
}
|
||||
306
src/CalcManager/CEngine/scifunc.cpp
Normal file
306
src/CalcManager/CEngine/scifunc.cpp
Normal 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);
|
||||
}
|
||||
|
||||
206
src/CalcManager/CEngine/scimath.cpp
Normal file
206
src/CalcManager/CEngine/scimath.cpp
Normal 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);
|
||||
}
|
||||
201
src/CalcManager/CEngine/scioper.cpp
Normal file
201
src/CalcManager/CEngine/scioper.cpp
Normal 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;
|
||||
}
|
||||
213
src/CalcManager/CEngine/sciset.cpp
Normal file
213
src/CalcManager/CEngine/sciset.cpp
Normal 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);
|
||||
}
|
||||
26
src/CalcManager/CalcException.h
Normal file
26
src/CalcManager/CalcException.h
Normal 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);
|
||||
}
|
||||
}
|
||||
335
src/CalcManager/CalcManager.vcxproj
Normal file
335
src/CalcManager/CalcManager.vcxproj
Normal 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>
|
||||
169
src/CalcManager/CalcManager.vcxproj.filters
Normal file
169
src/CalcManager/CalcManager.vcxproj.filters
Normal 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>
|
||||
80
src/CalcManager/CalculatorHistory.Cpp
Normal file
80
src/CalcManager/CalculatorHistory.Cpp
Normal 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();
|
||||
}
|
||||
49
src/CalcManager/CalculatorHistory.h
Normal file
49
src/CalcManager/CalculatorHistory.h
Normal 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;
|
||||
};
|
||||
}
|
||||
867
src/CalcManager/CalculatorManager.cpp
Normal file
867
src/CalcManager/CalculatorManager.cpp
Normal 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 };
|
||||
}
|
||||
}
|
||||
145
src/CalcManager/CalculatorManager.h
Normal file
145
src/CalcManager/CalculatorManager.h
Normal 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);
|
||||
};
|
||||
}
|
||||
22
src/CalcManager/CalculatorResource.h
Normal file
22
src/CalcManager/CalculatorResource.h
Normal 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;
|
||||
};
|
||||
}
|
||||
155
src/CalcManager/CalculatorVector.h
Normal file
155
src/CalcManager/CalculatorVector.h
Normal 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, ¤tPair);
|
||||
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
227
src/CalcManager/Command.h
Normal 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
|
||||
};
|
||||
}
|
||||
315
src/CalcManager/ExpressionCommand.cpp
Normal file
315
src/CalcManager/ExpressionCommand.cpp
Normal 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);
|
||||
}
|
||||
|
||||
88
src/CalcManager/ExpressionCommand.h
Normal file
88
src/CalcManager/ExpressionCommand.h
Normal 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 ¶Cmd) = 0;
|
||||
};
|
||||
55
src/CalcManager/ExpressionCommandInterface.h
Normal file
55
src/CalcManager/ExpressionCommandInterface.h
Normal 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;
|
||||
};
|
||||
214
src/CalcManager/Header Files/CCommand.h
Normal file
214
src/CalcManager/Header Files/CCommand.h
Normal 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
|
||||
|
||||
170
src/CalcManager/Header Files/CalcEngine.h
Normal file
170
src/CalcManager/Header Files/CalcEngine.h
Normal 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;
|
||||
};
|
||||
67
src/CalcManager/Header Files/CalcInput.h
Normal file
67
src/CalcManager/Header Files/CalcInput.h
Normal 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;
|
||||
};
|
||||
}
|
||||
13
src/CalcManager/Header Files/CalcUtils.h
Normal file
13
src/CalcManager/Header Files/CalcUtils.h
Normal 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);
|
||||
339
src/CalcManager/Header Files/EngineStrings.h
Normal file
339
src/CalcManager/Header Files/EngineStrings.h
Normal 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)
|
||||
};
|
||||
59
src/CalcManager/Header Files/History.h
Normal file
59
src/CalcManager/Header Files/History.h
Normal 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);
|
||||
};
|
||||
18
src/CalcManager/Header Files/ICalcDisplay.h
Normal file
18
src/CalcManager/Header Files/ICalcDisplay.h
Normal 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;
|
||||
};
|
||||
11
src/CalcManager/Header Files/IHistoryDisplay.h
Normal file
11
src/CalcManager/Header Files/IHistoryDisplay.h
Normal 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;
|
||||
};
|
||||
29
src/CalcManager/Header Files/Number.h
Normal file
29
src/CalcManager/Header Files/Number.h
Normal 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;
|
||||
};
|
||||
}
|
||||
28
src/CalcManager/Header Files/Rational.h
Normal file
28
src/CalcManager/Header Files/Rational.h
Normal 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;
|
||||
};
|
||||
}
|
||||
79
src/CalcManager/Header Files/scimath.h
Normal file
79
src/CalcManager/Header Files/scimath.h
Normal 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);
|
||||
82
src/CalcManager/Ratpack/CalcErr.h
Normal file
82
src/CalcManager/Ratpack/CalcErr.h
Normal 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)
|
||||
|
||||
367
src/CalcManager/Ratpack/basex.cpp
Normal file
367
src/CalcManager/Ratpack/basex.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
1486
src/CalcManager/Ratpack/conv.cpp
Normal file
1486
src/CalcManager/Ratpack/conv.cpp
Normal file
File diff suppressed because it is too large
Load Diff
540
src/CalcManager/Ratpack/exp.cpp
Normal file
540
src/CalcManager/Ratpack/exp.cpp
Normal 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;
|
||||
}
|
||||
258
src/CalcManager/Ratpack/fact.cpp
Normal file
258
src/CalcManager/Ratpack/fact.cpp
Normal 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);
|
||||
}
|
||||
|
||||
342
src/CalcManager/Ratpack/itrans.cpp
Normal file
342
src/CalcManager/Ratpack/itrans.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
160
src/CalcManager/Ratpack/itransh.cpp
Normal file
160
src/CalcManager/Ratpack/itransh.cpp
Normal 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);
|
||||
}
|
||||
|
||||
219
src/CalcManager/Ratpack/logic.cpp
Normal file
219
src/CalcManager/Ratpack/logic.cpp
Normal 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 );
|
||||
}
|
||||
|
||||
655
src/CalcManager/Ratpack/num.cpp
Normal file
655
src/CalcManager/Ratpack/num.cpp
Normal 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;
|
||||
}
|
||||
303
src/CalcManager/Ratpack/rat.cpp
Normal file
303
src/CalcManager/Ratpack/rat.cpp
Normal 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) );
|
||||
}
|
||||
|
||||
|
||||
482
src/CalcManager/Ratpack/ratconst.h
Normal file
482
src/CalcManager/Ratpack/ratconst.h
Normal 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, }
|
||||
};
|
||||
452
src/CalcManager/Ratpack/ratpak.h
Normal file
452
src/CalcManager/Ratpack/ratpak.h
Normal 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);
|
||||
719
src/CalcManager/Ratpack/support.cpp
Normal file
719
src/CalcManager/Ratpack/support.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
297
src/CalcManager/Ratpack/trans.cpp
Normal file
297
src/CalcManager/Ratpack/trans.cpp
Normal 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);
|
||||
}
|
||||
|
||||
231
src/CalcManager/Ratpack/transh.cpp
Normal file
231
src/CalcManager/Ratpack/transh.cpp
Normal 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);
|
||||
|
||||
}
|
||||
1081
src/CalcManager/UnitConverter.cpp
Normal file
1081
src/CalcManager/UnitConverter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
266
src/CalcManager/UnitConverter.h
Normal file
266
src/CalcManager/UnitConverter.h
Normal 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
4
src/CalcManager/pch.cpp
Normal 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
23
src/CalcManager/pch.h
Normal 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>
|
||||
11
src/CalcManager/targetver.h
Normal file
11
src/CalcManager/targetver.h
Normal 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>
|
||||
206
src/CalcViewModel/ApplicationViewModel.cpp
Normal file
206
src/CalcViewModel/ApplicationViewModel.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ApplicationViewModel.h"
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
#include "DateCalculatorViewModel.h"
|
||||
#include "Common\AppResourceProvider.h"
|
||||
#include "DataLoaders\CurrencyHttpClient.h"
|
||||
#include "DataLoaders\CurrencyDataLoader.h"
|
||||
#include "DataLoaders\UnitConverterDataLoader.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::DataLoaders;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace CalculationManager;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::System;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Utils;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace Windows::UI::ViewManagement;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
using namespace Windows::UI::Xaml::Input;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
|
||||
namespace CalculatorApp::ViewModel::ApplicationViewModelProperties
|
||||
{
|
||||
StringReference Mode(L"Mode");
|
||||
StringReference PreviousMode(L"PreviousMode");
|
||||
StringReference ClearMemoryVisibility(L"ClearMemoryVisibility");
|
||||
StringReference AppBarVisibility(L"AppBarVisibility");
|
||||
StringReference CategoryName(L"CategoryName");
|
||||
StringReference Categories(L"Categories");
|
||||
}
|
||||
|
||||
ApplicationViewModel::ApplicationViewModel() :
|
||||
m_CalculatorViewModel(nullptr),
|
||||
m_DateCalcViewModel(nullptr),
|
||||
m_ConverterViewModel(nullptr),
|
||||
m_PreviousMode(ViewMode::None),
|
||||
m_mode(ViewMode::None),
|
||||
m_categories(nullptr)
|
||||
{
|
||||
SetMenuCategories();
|
||||
}
|
||||
|
||||
void ApplicationViewModel::Mode::set(ViewMode value)
|
||||
{
|
||||
if (m_mode != value)
|
||||
{
|
||||
PreviousMode = m_mode;
|
||||
m_mode = value;
|
||||
OnModeChanged();
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::Mode);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::Categories::set(IObservableVector<NavCategoryGroup^>^ value)
|
||||
{
|
||||
if (m_categories != value)
|
||||
{
|
||||
m_categories = value;
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::Categories);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::Initialize(ViewMode mode)
|
||||
{
|
||||
if (!NavCategory::IsValidViewMode(mode))
|
||||
{
|
||||
mode = ViewMode::Standard;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Mode = mode;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e);
|
||||
if (!TryRecoverFromNavigationModeFailure())
|
||||
{
|
||||
// Could not navigate to standard mode either.
|
||||
// Throw the original exception so we have a good stack to debug.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (Exception^ e)
|
||||
{
|
||||
TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, e);
|
||||
if (!TryRecoverFromNavigationModeFailure())
|
||||
{
|
||||
// Could not navigate to standard mode either.
|
||||
// Throw the original exception so we have a good stack to debug.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplicationViewModel::TryRecoverFromNavigationModeFailure()
|
||||
{
|
||||
// Here we are simply trying to recover from being unable to navigate to a mode.
|
||||
// Try falling back to standard mode and if there are *any* exceptions, we should
|
||||
// fail because something is seriously wrong.
|
||||
try
|
||||
{
|
||||
Mode = ViewMode::Standard;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::OnModeChanged()
|
||||
{
|
||||
assert(NavCategory::IsValidViewMode(m_mode));
|
||||
TraceLogger::GetInstance().LogModeChangeBegin(m_PreviousMode, m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (NavCategory::IsCalculatorViewMode(m_mode))
|
||||
{
|
||||
TraceLogger::GetInstance().LogCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (!m_CalculatorViewModel)
|
||||
{
|
||||
m_CalculatorViewModel = ref new StandardCalculatorViewModel();
|
||||
}
|
||||
m_CalculatorViewModel->SetCalculatorType(m_mode);
|
||||
}
|
||||
else if (NavCategory::IsDateCalculatorViewMode(m_mode))
|
||||
{
|
||||
TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (!m_DateCalcViewModel)
|
||||
{
|
||||
m_DateCalcViewModel = ref new DateCalculatorViewModel();
|
||||
}
|
||||
}
|
||||
else if (NavCategory::IsConverterViewMode(m_mode))
|
||||
{
|
||||
TraceLogger::GetInstance().LogConverterModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (!m_ConverterViewModel)
|
||||
{
|
||||
auto dataLoader = make_shared<UnitConverterDataLoader>(ref new GeographicRegion());
|
||||
auto currencyDataLoader = make_shared<CurrencyDataLoader>(make_unique<CurrencyHttpClient>());
|
||||
m_ConverterViewModel = ref new UnitConverterViewModel(make_shared<UnitConversionManager::UnitConverter>(dataLoader, currencyDataLoader));
|
||||
}
|
||||
|
||||
m_ConverterViewModel->Mode = m_mode;
|
||||
}
|
||||
|
||||
auto resProvider = AppResourceProvider::GetInstance();
|
||||
CategoryName = resProvider.GetResourceString(NavCategory::GetNameResourceKey(m_mode));
|
||||
|
||||
// This is the only place where a ViewMode enum should be cast to an int.
|
||||
//
|
||||
// Save the changed mode, so that the new window launches in this mode.
|
||||
// Don't save until after we have adjusted to the new mode, so we don't save a mode that fails to load.
|
||||
ApplicationData::Current->LocalSettings->Values->Insert(ApplicationViewModelProperties::Mode, NavCategory::Serialize(m_mode));
|
||||
|
||||
TraceLogger::GetInstance().LogModeChangeEnd(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::ClearMemoryVisibility);
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::AppBarVisibility);
|
||||
}
|
||||
|
||||
void ApplicationViewModel::OnCopyCommand(Object^ parameter)
|
||||
{
|
||||
if (NavCategory::IsConverterViewMode(m_mode))
|
||||
{
|
||||
ConverterViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
else if (NavCategory::IsDateCalculatorViewMode(m_mode))
|
||||
{
|
||||
DateCalcViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculatorViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::OnPasteCommand(Object^ parameter)
|
||||
{
|
||||
if (NavCategory::IsConverterViewMode(m_mode))
|
||||
{
|
||||
ConverterViewModel->OnPasteCommand(parameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculatorViewModel->OnPasteCommand(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::SetMenuCategories()
|
||||
{
|
||||
// Use the Categories property instead of the backing variable
|
||||
// because we want to take advantage of binding updates and
|
||||
// property setter logic.
|
||||
Categories = NavCategoryGroup::CreateMenuOptions();
|
||||
}
|
||||
96
src/CalcViewModel/ApplicationViewModel.h
Normal file
96
src/CalcViewModel/ApplicationViewModel.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
#include "DateCalculatorViewModel.h"
|
||||
#include "UnitConverterViewModel.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
namespace ApplicationViewModelProperties
|
||||
{
|
||||
extern Platform::StringReference Mode;
|
||||
extern Platform::StringReference PreviousMode;
|
||||
extern Platform::StringReference ClearMemoryVisibility;
|
||||
extern Platform::StringReference AppBarVisibility;
|
||||
extern Platform::StringReference CategoryName;
|
||||
extern Platform::StringReference Categories;
|
||||
}
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
ApplicationViewModel();
|
||||
|
||||
void Initialize(CalculatorApp::Common::ViewMode mode); // Use for first init, use deserialize for rehydration
|
||||
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel^, CalculatorViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel^, DateCalcViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::ViewModel::UnitConverterViewModel^, ConverterViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, CategoryName);
|
||||
|
||||
COMMAND_FOR_METHOD(CopyCommand, ApplicationViewModel::OnCopyCommand);
|
||||
COMMAND_FOR_METHOD(PasteCommand, ApplicationViewModel::OnPasteCommand);
|
||||
|
||||
property CalculatorApp::Common::ViewMode Mode
|
||||
{
|
||||
CalculatorApp::Common::ViewMode get()
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
void set(CalculatorApp::Common::ViewMode value);
|
||||
}
|
||||
|
||||
property Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ Categories
|
||||
{
|
||||
Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ get()
|
||||
{
|
||||
return m_categories;
|
||||
}
|
||||
|
||||
void set(Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ value);
|
||||
}
|
||||
|
||||
property Windows::UI::Xaml::Visibility ClearMemoryVisibility
|
||||
{
|
||||
Windows::UI::Xaml::Visibility get()
|
||||
{
|
||||
return CalculatorApp::Common::NavCategory::IsCalculatorViewMode(Mode)
|
||||
? Windows::UI::Xaml::Visibility::Visible
|
||||
: Windows::UI::Xaml::Visibility::Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
property Windows::UI::Xaml::Visibility AppBarVisibility
|
||||
{
|
||||
Windows::UI::Xaml::Visibility get()
|
||||
{
|
||||
return CalculatorApp::Common::NavCategory::IsCalculatorViewMode(Mode)
|
||||
? Windows::UI::Xaml::Visibility::Visible
|
||||
: Windows::UI::Xaml::Visibility::Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool TryRecoverFromNavigationModeFailure();
|
||||
|
||||
void OnModeChanged();
|
||||
|
||||
void OnCopyCommand(Platform::Object^ parameter);
|
||||
void OnPasteCommand(Platform::Object^ parameter);
|
||||
|
||||
void SetMenuCategories();
|
||||
|
||||
CalculatorApp::Common::ViewMode m_mode;
|
||||
Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ m_categories;
|
||||
};
|
||||
}
|
||||
}
|
||||
409
src/CalcViewModel/CalcViewModel.vcxproj
Normal file
409
src/CalcViewModel/CalcViewModel.vcxproj
Normal file
@@ -0,0 +1,409 @@
|
||||
<?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>{90e9761d-9262-4773-942d-caeae75d7140}</ProjectGuid>
|
||||
<Keyword>StaticLibrary</Keyword>
|
||||
<RootNamespace>CalcViewModel</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<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="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<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>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(IsStoreBuild)' == 'True'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/DSEND_TELEMETRY %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ApplicationViewModel.h" />
|
||||
<ClInclude Include="Common\AlwaysSelectedCollectionView.h" />
|
||||
<ClInclude Include="Common\AppResourceProvider.h" />
|
||||
<ClInclude Include="Common\Automation\INarratorAnnouncementHost.h" />
|
||||
<ClInclude Include="Common\Automation\LiveRegionHost.h" />
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncement.h" />
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncementHostFactory.h" />
|
||||
<ClInclude Include="Common\Automation\NarratorNotifier.h" />
|
||||
<ClInclude Include="Common\Automation\NotificationHost.h" />
|
||||
<ClInclude Include="Common\BindableBase.h" />
|
||||
<ClInclude Include="Common\CalculatorButtonPressedEventArgs.h" />
|
||||
<ClInclude Include="Common\CalculatorButtonUser.h" />
|
||||
<ClInclude Include="Common\CalculatorDisplay.h" />
|
||||
<ClInclude Include="Common\ConversionResultTaskHelper.h" />
|
||||
<ClInclude Include="Common\CopyPasteManager.h" />
|
||||
<ClInclude Include="Common\DateCalculator.h" />
|
||||
<ClInclude Include="Common\DelegateCommand.h" />
|
||||
<ClInclude Include="Common\DisplayExpressionToken.h" />
|
||||
<ClInclude Include="Common\EngineResourceProvider.h" />
|
||||
<ClInclude Include="Common\ExpressionCommandDeserializer.h" />
|
||||
<ClInclude Include="Common\ExpressionCommandSerializer.h" />
|
||||
<ClInclude Include="Common\KeyboardShortcutManager.h" />
|
||||
<ClInclude Include="Common\LocalizationService.h" />
|
||||
<ClInclude Include="Common\LocalizationSettings.h" />
|
||||
<ClInclude Include="Common\LocalizationStringUtil.h" />
|
||||
<ClInclude Include="Common\MyVirtualKey.h" />
|
||||
<ClInclude Include="Common\NavCategory.h" />
|
||||
<ClInclude Include="Common\NetworkManager.h" />
|
||||
<ClInclude Include="Common\TraceActivity.h" />
|
||||
<ClInclude Include="Common\TraceLogger.h" />
|
||||
<ClInclude Include="Common\Utils.h" />
|
||||
<ClInclude Include="Common\ValidatingConverters.h" />
|
||||
<ClInclude Include="DataLoaders\CurrencyDataLoader.h" />
|
||||
<ClInclude Include="DataLoaders\CurrencyHttpClient.h" />
|
||||
<ClInclude Include="DataLoaders\ICurrencyHttpClient.h" />
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h" />
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h" />
|
||||
<ClInclude Include="DateCalculatorViewModel.h" />
|
||||
<ClInclude Include="HistoryItemViewModel.h" />
|
||||
<ClInclude Include="HistoryViewModel.h" />
|
||||
<ClInclude Include="MemoryItemViewModel.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="StandardCalculatorViewModel.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="UnitConverterViewModel.h" />
|
||||
<ClInclude Include="ViewState.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ApplicationViewModel.cpp" />
|
||||
<ClCompile Include="Common\AppResourceProvider.cpp" />
|
||||
<ClCompile Include="Common\Automation\LiveRegionHost.cpp" />
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp" />
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncementHostFactory.cpp" />
|
||||
<ClCompile Include="Common\Automation\NarratorNotifier.cpp" />
|
||||
<ClCompile Include="Common\Automation\NotificationHost.cpp" />
|
||||
<ClCompile Include="Common\BindableBase.cpp" />
|
||||
<ClCompile Include="Common\CalculatorButtonPressedEventArgs.cpp" />
|
||||
<ClCompile Include="Common\CalculatorDisplay.cpp" />
|
||||
<ClCompile Include="Common\ConversionResultTaskHelper.cpp" />
|
||||
<ClCompile Include="Common\CopyPasteManager.cpp" />
|
||||
<ClCompile Include="Common\DateCalculator.cpp" />
|
||||
<ClCompile Include="Common\EngineResourceProvider.cpp" />
|
||||
<ClCompile Include="Common\ExpressionCommandDeserializer.cpp" />
|
||||
<ClCompile Include="Common\ExpressionCommandSerializer.cpp" />
|
||||
<ClCompile Include="Common\KeyboardShortcutManager.cpp" />
|
||||
<ClCompile Include="Common\LocalizationService.cpp" />
|
||||
<ClCompile Include="Common\NavCategory.cpp" />
|
||||
<ClCompile Include="Common\NetworkManager.cpp" />
|
||||
<ClCompile Include="Common\TraceLogger.cpp" />
|
||||
<ClCompile Include="Common\Utils.cpp" />
|
||||
<ClCompile Include="DataLoaders\CurrencyDataLoader.cpp" />
|
||||
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp" />
|
||||
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp" />
|
||||
<ClCompile Include="DateCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="HistoryItemViewModel.cpp" />
|
||||
<ClCompile Include="HistoryViewModel.cpp" />
|
||||
<ClCompile Include="MemoryItemViewModel.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="StandardCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="UnitConverterViewModel.cpp" />
|
||||
<ClCompile Include="ViewState.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CalcManager\CalcManager.vcxproj">
|
||||
<Project>{311e866d-8b93-4609-a691-265941fee101}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DataLoaders\DefaultFromToCurrency.json" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
227
src/CalcViewModel/CalcViewModel.vcxproj.filters
Normal file
227
src/CalcViewModel/CalcViewModel.vcxproj.filters
Normal file
@@ -0,0 +1,227 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.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="Common">
|
||||
<UniqueIdentifier>{1daab7c4-63f6-4266-a259-f34acad66d09}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Common\Automation">
|
||||
<UniqueIdentifier>{8d4edf06-c312-4312-978a-b6c2beb8295a}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="DataLoaders">
|
||||
<UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="ApplicationViewModel.cpp" />
|
||||
<ClCompile Include="DateCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="HistoryItemViewModel.cpp" />
|
||||
<ClCompile Include="HistoryViewModel.cpp" />
|
||||
<ClCompile Include="MemoryItemViewModel.cpp" />
|
||||
<ClCompile Include="StandardCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="UnitConverterViewModel.cpp" />
|
||||
<ClCompile Include="ViewState.cpp" />
|
||||
<ClCompile Include="Common\AppResourceProvider.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\BindableBase.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\CalculatorButtonPressedEventArgs.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\CalculatorDisplay.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\ConversionResultTaskHelper.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\CopyPasteManager.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\DateCalculator.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\EngineResourceProvider.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\ExpressionCommandDeserializer.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\ExpressionCommandSerializer.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\KeyboardShortcutManager.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\LocalizationService.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\NavCategory.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\NetworkManager.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\TraceLogger.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Utils.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\LiveRegionHost.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncementHostFactory.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NarratorNotifier.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NotificationHost.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DataLoaders\CurrencyDataLoader.cpp">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="ApplicationViewModel.h" />
|
||||
<ClInclude Include="DateCalculatorViewModel.h" />
|
||||
<ClInclude Include="HistoryItemViewModel.h" />
|
||||
<ClInclude Include="HistoryViewModel.h" />
|
||||
<ClInclude Include="MemoryItemViewModel.h" />
|
||||
<ClInclude Include="StandardCalculatorViewModel.h" />
|
||||
<ClInclude Include="UnitConverterViewModel.h" />
|
||||
<ClInclude Include="ViewState.h" />
|
||||
<ClInclude Include="Common\AlwaysSelectedCollectionView.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\AppResourceProvider.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\BindableBase.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CalculatorButtonPressedEventArgs.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CalculatorButtonUser.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CalculatorDisplay.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ConversionResultTaskHelper.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CopyPasteManager.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\DateCalculator.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\DelegateCommand.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\DisplayExpressionToken.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\EngineResourceProvider.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ExpressionCommandDeserializer.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ExpressionCommandSerializer.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\KeyboardShortcutManager.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\LocalizationService.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\LocalizationSettings.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\LocalizationStringUtil.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\MyVirtualKey.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\NavCategory.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\NetworkManager.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\TraceLogger.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Utils.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ValidatingConverters.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\INarratorAnnouncementHost.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\LiveRegionHost.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncement.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncementHostFactory.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NarratorNotifier.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NotificationHost.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\CurrencyDataLoader.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\CurrencyHttpClient.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\ICurrencyHttpClient.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\TraceActivity.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DataLoaders\DefaultFromToCurrency.json">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
287
src/CalcViewModel/Common/AlwaysSelectedCollectionView.h
Normal file
287
src/CalcViewModel/Common/AlwaysSelectedCollectionView.h
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp { namespace Common
|
||||
{
|
||||
ref class AlwaysSelectedCollectionView sealed:
|
||||
public Windows::UI::Xaml::DependencyObject,
|
||||
public Windows::UI::Xaml::Data::ICollectionView
|
||||
{
|
||||
internal:
|
||||
AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector^ source):
|
||||
m_currentPosition(-1)
|
||||
{
|
||||
m_source = source;
|
||||
|
||||
Windows::UI::Xaml::Interop::IBindableObservableVector^ observable = dynamic_cast<Windows::UI::Xaml::Interop::IBindableObservableVector^>(source);
|
||||
if (observable)
|
||||
{
|
||||
observable->VectorChanged +=
|
||||
ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler(this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// ICollectionView
|
||||
// Not implemented methods
|
||||
virtual WF::IAsyncOperation<Windows::UI::Xaml::Data::LoadMoreItemsResult>^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
property Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ CollectionGroups
|
||||
{
|
||||
virtual Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get
|
||||
{
|
||||
return ref new Platform::Collections::Vector<Platform::Object^>();
|
||||
}
|
||||
}
|
||||
property bool HasMoreItems
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementented methods
|
||||
virtual bool MoveCurrentTo(Platform::Object^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo
|
||||
{
|
||||
if (item)
|
||||
{
|
||||
unsigned int newCurrentPosition = 0;
|
||||
bool result = m_source->IndexOf(item, &newCurrentPosition);
|
||||
if (result)
|
||||
{
|
||||
m_currentPosition = newCurrentPosition;
|
||||
m_currentChanged(this, nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The item is not in the collection
|
||||
// We're going to schedule a call back later so we
|
||||
// restore the selection to the way we wanted it to begin with
|
||||
if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
|
||||
{
|
||||
this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
ref new Windows::UI::Core::DispatchedHandler(
|
||||
[this]()
|
||||
{
|
||||
m_currentChanged(this, nullptr);
|
||||
}));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition
|
||||
{
|
||||
if (index < 0 || index >= static_cast<int>(m_source->Size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentPosition = index;
|
||||
m_currentChanged(this, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
property Platform::Object^ CurrentItem
|
||||
{
|
||||
virtual Platform::Object^ get() = Windows::UI::Xaml::Data::ICollectionView::CurrentItem::get
|
||||
{
|
||||
if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
|
||||
{
|
||||
return m_source->GetAt(m_currentPosition);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
property int CurrentPosition
|
||||
{
|
||||
virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get
|
||||
{
|
||||
return m_currentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsCurrentAfterLast
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get
|
||||
{
|
||||
return m_currentPosition >= static_cast<int>(m_source->Size);
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsCurrentBeforeFirst
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get
|
||||
{
|
||||
return m_currentPosition < 0;
|
||||
}
|
||||
}
|
||||
|
||||
event WF::EventHandler<Platform::Object^>^ CurrentChanged
|
||||
{
|
||||
virtual WF::EventRegistrationToken add(WF::EventHandler<Platform::Object^>^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::add
|
||||
{
|
||||
return m_currentChanged += handler;
|
||||
}
|
||||
virtual void remove(WF::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::remove
|
||||
{
|
||||
m_currentChanged -= token;
|
||||
}
|
||||
}
|
||||
event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ CurrentChanging
|
||||
{
|
||||
virtual WF::EventRegistrationToken add(Windows::UI::Xaml::Data::CurrentChangingEventHandler^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::add
|
||||
{
|
||||
return m_currentChanging += handler;
|
||||
}
|
||||
virtual void remove(WF::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::remove
|
||||
{
|
||||
m_currentChanging -= token;
|
||||
}
|
||||
}
|
||||
|
||||
// IVector<Object^>
|
||||
// Not implemented methods
|
||||
virtual void Append(Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::Append
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void Clear() = Windows::Foundation::Collections::IVector<Platform::Object^>::Clear
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual unsigned int GetMany(unsigned int /*startIndex*/, Platform::WriteOnlyArray<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetMany
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual Windows::Foundation::Collections::IVectorView<Platform::Object^>^ GetView() = Windows::Foundation::Collections::IVector<Platform::Object^>::GetView
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void InsertAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::InsertAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAtEnd
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void ReplaceAll(const Platform::Array<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::ReplaceAll
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void SetAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::SetAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
|
||||
// Implemented methods
|
||||
virtual Platform::Object^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetAt
|
||||
{
|
||||
return m_source->GetAt(index);
|
||||
}
|
||||
|
||||
virtual bool IndexOf(Platform::Object^ item, unsigned int* index) = Windows::Foundation::Collections::IVector<Platform::Object^>::IndexOf
|
||||
{
|
||||
return m_source->IndexOf(item, index);
|
||||
}
|
||||
|
||||
property unsigned int Size
|
||||
{
|
||||
virtual unsigned int get() = Windows::Foundation::Collections::IVector<Platform::Object^>::Size::get
|
||||
{
|
||||
return m_source->Size;
|
||||
}
|
||||
}
|
||||
|
||||
// IObservableVector<Object^>
|
||||
event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ VectorChanged
|
||||
{
|
||||
virtual WF::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ handler) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::add
|
||||
{
|
||||
return m_vectorChanged += handler;
|
||||
}
|
||||
virtual void remove(WF::EventRegistrationToken token) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::remove
|
||||
{
|
||||
m_vectorChanged -= token;
|
||||
}
|
||||
}
|
||||
|
||||
// IIterable<Object^>
|
||||
// Not implemented
|
||||
virtual Windows::Foundation::Collections::IIterator<Platform::Object^>^ First() = Windows::Foundation::Collections::IIterable<Platform::Object^>::First
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ source, Platform::Object^ e)
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorChangedEventArgs^ args = safe_cast<Windows::Foundation::Collections::IVectorChangedEventArgs^>(e);
|
||||
m_vectorChanged(this, args);
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Interop::IBindableVector^ m_source;
|
||||
int m_currentPosition;
|
||||
event WF::EventHandler<Platform::Object^>^ m_currentChanged;
|
||||
event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ m_currentChanging;
|
||||
event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ m_vectorChanged;
|
||||
};
|
||||
|
||||
public ref class AlwaysSelectedCollectionViewConverter sealed: public Windows::UI::Xaml::Data::IValueConverter
|
||||
{
|
||||
public:
|
||||
AlwaysSelectedCollectionViewConverter()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual Platform::Object^ Convert(
|
||||
Platform::Object^ value,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert
|
||||
{
|
||||
auto result = dynamic_cast<Windows::UI::Xaml::Interop::IBindableVector^>(value);
|
||||
if (result)
|
||||
{
|
||||
return ref new AlwaysSelectedCollectionView(result);
|
||||
}
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert
|
||||
}
|
||||
|
||||
virtual Platform::Object^ ConverBack(
|
||||
Platform::Object^ /*value*/,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack
|
||||
{
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue;
|
||||
}
|
||||
};
|
||||
}}
|
||||
33
src/CalcViewModel/Common/AppResourceProvider.cpp
Normal file
33
src/CalcViewModel/Common/AppResourceProvider.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppResourceProvider.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace CalculatorApp;
|
||||
|
||||
AppResourceProvider::AppResourceProvider()
|
||||
{
|
||||
m_stringResLoader = ResourceLoader::GetForViewIndependentUse();
|
||||
m_cEngineStringResLoader = ResourceLoader::GetForViewIndependentUse(L"CEngineStrings");
|
||||
}
|
||||
|
||||
AppResourceProvider & AppResourceProvider::GetInstance()
|
||||
{
|
||||
static AppResourceProvider s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
String^ AppResourceProvider::GetResourceString(_In_ String^ key)
|
||||
{
|
||||
return m_stringResLoader->GetString(key);
|
||||
}
|
||||
|
||||
String^ AppResourceProvider::GetCEngineString(_In_ String^ key)
|
||||
{
|
||||
return m_cEngineStringResLoader->GetString(key);
|
||||
}
|
||||
20
src/CalcViewModel/Common/AppResourceProvider.h
Normal file
20
src/CalcViewModel/Common/AppResourceProvider.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
class AppResourceProvider
|
||||
{
|
||||
public:
|
||||
static AppResourceProvider & GetInstance();
|
||||
Platform::String^ GetResourceString(_In_ Platform::String^ key);
|
||||
Platform::String^ GetCEngineString(_In_ Platform::String^ key);
|
||||
|
||||
private:
|
||||
AppResourceProvider();
|
||||
Windows::ApplicationModel::Resources::ResourceLoader^ m_stringResLoader;
|
||||
Windows::ApplicationModel::Resources::ResourceLoader^ m_cEngineStringResLoader;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "NarratorAnnouncement.h"
|
||||
|
||||
// Declaration of the INarratorAnnouncementHost interface.
|
||||
// This interface exists to hide the concrete announcement host
|
||||
// being used. Depending on the version of the OS the app is running on,
|
||||
// the app may need a host that uses LiveRegionChanged or RaiseNotification.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public interface class INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
// Is the host available on this OS.
|
||||
bool IsHostAvailable();
|
||||
|
||||
// Make a new instance of a concrete host.
|
||||
INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
// Make an announcement using the concrete host's preferred method.
|
||||
void Announce(NarratorAnnouncement^ announcement);
|
||||
};
|
||||
}
|
||||
41
src/CalcViewModel/Common/Automation/LiveRegionHost.cpp
Normal file
41
src/CalcViewModel/Common/Automation/LiveRegionHost.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "LiveRegionHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
LiveRegionHost::LiveRegionHost() :
|
||||
m_host(nullptr)
|
||||
{}
|
||||
|
||||
bool LiveRegionHost::IsHostAvailable()
|
||||
{
|
||||
// LiveRegion is always available.
|
||||
return true;
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ LiveRegionHost::MakeHost()
|
||||
{
|
||||
return ref new LiveRegionHost();
|
||||
}
|
||||
|
||||
void LiveRegionHost::Announce(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
if (m_host == nullptr)
|
||||
{
|
||||
m_host = ref new TextBlock();
|
||||
AutomationProperties::SetLiveSetting(m_host, AutomationLiveSetting::Assertive);
|
||||
}
|
||||
|
||||
AutomationProperties::SetName(m_host, announcement->Announcement);
|
||||
AutomationPeer^ peer = FrameworkElementAutomationPeer::FromElement(m_host);
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged);
|
||||
}
|
||||
}
|
||||
32
src/CalcViewModel/Common/Automation/LiveRegionHost.h
Normal file
32
src/CalcViewModel/Common/Automation/LiveRegionHost.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the LiveRegionHost class.
|
||||
// This class announces NarratorAnnouncements using the LiveRegionChanged event.
|
||||
// This event is unreliable and should be deprecated in favor of the new
|
||||
// RaiseNotification API in RS3.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
// This class exists so that the app can run on RS2 and use LiveRegions
|
||||
// to host notifiactions on those builds.
|
||||
// When the app switches to min version RS3, this class can be removed
|
||||
// and the app will switch to using the Notification API.
|
||||
// TODO - MSFT 12735088
|
||||
public ref class LiveRegionHost sealed : public INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
LiveRegionHost();
|
||||
|
||||
virtual bool IsHostAvailable();
|
||||
virtual INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
virtual void Announce(NarratorAnnouncement^ announcement);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::UIElement^ m_host;
|
||||
};
|
||||
}
|
||||
144
src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp
Normal file
144
src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NarratorAnnouncement.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Platform;
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
namespace CalculatorActivityIds
|
||||
{
|
||||
StringReference DisplayUpdated(L"DisplayUpdated");
|
||||
StringReference MaxDigitsReached(L"MaxDigitsReached");
|
||||
StringReference MemoryCleared(L"MemoryCleared");
|
||||
StringReference MemoryItemChanged(L"MemorySlotChanged");
|
||||
StringReference MemoryItemAdded(L"MemorySlotAdded");
|
||||
StringReference HistoryCleared(L"HistoryCleared");
|
||||
StringReference CategoryNameChanged(L"CategoryNameChanged");
|
||||
StringReference UpdateCurrencyRates(L"UpdateCurrencyRates");
|
||||
StringReference DisplayCopied(L"DisplayCopied");
|
||||
}
|
||||
}
|
||||
|
||||
NarratorAnnouncement::NarratorAnnouncement(
|
||||
String^ announcement,
|
||||
String^ activityId,
|
||||
AutomationNotificationKind kind,
|
||||
AutomationNotificationProcessing processing)
|
||||
:
|
||||
m_announcement(announcement),
|
||||
m_activityId(activityId),
|
||||
m_kind(kind),
|
||||
m_processing(processing)
|
||||
{}
|
||||
|
||||
String^ NarratorAnnouncement::Announcement::get()
|
||||
{
|
||||
return m_announcement;
|
||||
}
|
||||
|
||||
String^ NarratorAnnouncement::ActivityId::get()
|
||||
{
|
||||
return m_activityId;
|
||||
}
|
||||
|
||||
AutomationNotificationKind NarratorAnnouncement::Kind::get()
|
||||
{
|
||||
return m_kind;
|
||||
}
|
||||
|
||||
AutomationNotificationProcessing NarratorAnnouncement::Processing::get()
|
||||
{
|
||||
return m_processing;
|
||||
}
|
||||
|
||||
bool NarratorAnnouncement::IsValid(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
return announcement != nullptr
|
||||
&& announcement->Announcement != nullptr
|
||||
&& !announcement->Announcement->IsEmpty();
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::DisplayUpdated,
|
||||
AutomationNotificationKind::Other,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MaxDigitsReached,
|
||||
AutomationNotificationKind::Other,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryClearedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MemoryCleared,
|
||||
AutomationNotificationKind::ItemRemoved,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryItemChangedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MemoryItemChanged,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::MostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryItemAddedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MemoryItemAdded,
|
||||
AutomationNotificationKind::ItemAdded,
|
||||
AutomationNotificationProcessing::MostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetHistoryClearedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::HistoryCleared,
|
||||
AutomationNotificationKind::ItemRemoved,
|
||||
AutomationNotificationProcessing::MostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetCategoryNameChangedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::CategoryNameChanged,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetUpdateCurrencyRatesAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::UpdateCurrencyRates,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetDisplayCopiedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::DisplayCopied,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
94
src/CalcViewModel/Common/Automation/NarratorAnnouncement.h
Normal file
94
src/CalcViewModel/Common/Automation/NarratorAnnouncement.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
// These enum types are copied from the types available in
|
||||
// Windows::UI::Xaml::Automation::Peers in the RS3 SDK.
|
||||
// When this app switches to min version RS3, these custom
|
||||
// enums should be removed and the Windows types should be used
|
||||
// instead.
|
||||
// TODO - MSFT 12735088
|
||||
public enum class AutomationNotificationKind
|
||||
{
|
||||
ItemAdded = 0,
|
||||
ItemRemoved = 1,
|
||||
ActionCompleted = 2,
|
||||
ActionAborted = 3,
|
||||
Other = 4
|
||||
};
|
||||
|
||||
public enum class AutomationNotificationProcessing
|
||||
{
|
||||
ImportantAll = 0,
|
||||
ImportantMostRecent = 1,
|
||||
All = 2,
|
||||
MostRecent = 3,
|
||||
CurrentThenMostRecent = 4
|
||||
};
|
||||
|
||||
public ref class NarratorAnnouncement sealed
|
||||
{
|
||||
public:
|
||||
property Platform::String^ Announcement
|
||||
{
|
||||
Platform::String^ get();
|
||||
}
|
||||
|
||||
property Platform::String^ ActivityId
|
||||
{
|
||||
Platform::String^ get();
|
||||
}
|
||||
|
||||
property AutomationNotificationKind Kind
|
||||
{
|
||||
AutomationNotificationKind get();
|
||||
}
|
||||
|
||||
property AutomationNotificationProcessing Processing
|
||||
{
|
||||
AutomationNotificationProcessing get();
|
||||
}
|
||||
|
||||
static bool IsValid(NarratorAnnouncement^ announcement);
|
||||
|
||||
private:
|
||||
// Make CalculatorAnnouncement a friend class so it is the only
|
||||
// class that can access the private constructor.
|
||||
friend class CalculatorAnnouncement;
|
||||
|
||||
NarratorAnnouncement(
|
||||
Platform::String^ announcement,
|
||||
Platform::String^ activityId,
|
||||
AutomationNotificationKind kind,
|
||||
AutomationNotificationProcessing processing);
|
||||
|
||||
Platform::String^ m_announcement;
|
||||
Platform::String^ m_activityId;
|
||||
AutomationNotificationKind m_kind;
|
||||
AutomationNotificationProcessing m_processing;
|
||||
};
|
||||
|
||||
// CalculatorAnnouncement is intended to contain only static methods
|
||||
// that return announcements made for the Calculator app.
|
||||
class CalculatorAnnouncement
|
||||
{
|
||||
public:
|
||||
static NarratorAnnouncement^ GetDisplayUpdatedAnnouncement(Platform::String^ announcement);
|
||||
static NarratorAnnouncement^ GetMaxDigitsReachedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetMemoryClearedAnnouncement(Platform::String^ announcement);
|
||||
static NarratorAnnouncement^ GetMemoryItemChangedAnnouncement(Platform::String^ announcement);
|
||||
static NarratorAnnouncement^ GetMemoryItemAddedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetHistoryClearedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetCategoryNameChangedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetUpdateCurrencyRatesAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetDisplayCopiedAnnouncement(Platform::String^ announcement);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NarratorAnnouncementHostFactory.h"
|
||||
#include "NotificationHost.h"
|
||||
#include "LiveRegionHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace std;
|
||||
|
||||
INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::s_hostProducer;
|
||||
vector<INarratorAnnouncementHost^> NarratorAnnouncementHostFactory::s_hosts;
|
||||
|
||||
// This static variable is used only to call the initialization function, to initialize the other static variables.
|
||||
int NarratorAnnouncementHostFactory::s_init = NarratorAnnouncementHostFactory::Initialize();
|
||||
int NarratorAnnouncementHostFactory::Initialize()
|
||||
{
|
||||
RegisterHosts();
|
||||
NarratorAnnouncementHostFactory::s_hostProducer = GetHostProducer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For now, there are two type of announcement hosts.
|
||||
// We'd prefer to use Notification if it's available and fallback to LiveRegion
|
||||
// if not. The availabilty of the host depends on the version of the OS the app is running on.
|
||||
// When the app switches to min version RS3, the LiveRegionHost can be removed and we will always
|
||||
// use NotificationHost.
|
||||
// TODO - MSFT 12735088
|
||||
void NarratorAnnouncementHostFactory::RegisterHosts()
|
||||
{
|
||||
// The host that will be used is the first available host,
|
||||
// therefore, order of hosts is important here.
|
||||
NarratorAnnouncementHostFactory::s_hosts = {
|
||||
ref new NotificationHost(),
|
||||
ref new LiveRegionHost()
|
||||
};
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::GetHostProducer()
|
||||
{
|
||||
for (INarratorAnnouncementHost^ host : NarratorAnnouncementHostFactory::s_hosts)
|
||||
{
|
||||
if (host->IsHostAvailable())
|
||||
{
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
assert(false && L"No suitable AnnouncementHost was found.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::MakeHost()
|
||||
{
|
||||
if (NarratorAnnouncementHostFactory::s_hostProducer == nullptr)
|
||||
{
|
||||
assert(false && L"No host producer has been assigned.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NarratorAnnouncementHostFactory::s_hostProducer->MakeHost();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the NarratorAnnouncementHostFactory class.
|
||||
// This class exists to hide the construction of a concrete INarratorAnnouncementHost.
|
||||
// Depending on the version of the OS the app is running on, the factory will return
|
||||
// an announcement host appropriate for that version.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
class NarratorAnnouncementHostFactory
|
||||
{
|
||||
public:
|
||||
static INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
private:
|
||||
NarratorAnnouncementHostFactory() {}
|
||||
|
||||
static int Initialize();
|
||||
static void RegisterHosts();
|
||||
static INarratorAnnouncementHost^ GetHostProducer();
|
||||
|
||||
private:
|
||||
static int s_init;
|
||||
static INarratorAnnouncementHost^ s_hostProducer;
|
||||
static std::vector<INarratorAnnouncementHost^> s_hosts;
|
||||
};
|
||||
}
|
||||
50
src/CalcViewModel/Common/Automation/NarratorNotifier.cpp
Normal file
50
src/CalcViewModel/Common/Automation/NarratorNotifier.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// Implementation of the NarratorNotifier class.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NarratorNotifier.h"
|
||||
#include "NarratorAnnouncementHostFactory.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Platform;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
|
||||
DependencyProperty^ NarratorNotifier::s_announcementProperty;
|
||||
|
||||
NarratorNotifier::NarratorNotifier()
|
||||
{
|
||||
m_announcementHost = NarratorAnnouncementHostFactory::MakeHost();
|
||||
}
|
||||
|
||||
void NarratorNotifier::Announce(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
if (NarratorAnnouncement::IsValid(announcement)
|
||||
&& m_announcementHost != nullptr)
|
||||
{
|
||||
m_announcementHost->Announce(announcement);
|
||||
}
|
||||
}
|
||||
|
||||
void NarratorNotifier::RegisterDependencyProperties()
|
||||
{
|
||||
s_announcementProperty = DependencyProperty::Register(
|
||||
L"Announcement", // The name of the dependency property.
|
||||
NarratorAnnouncement::typeid, // The type of the dependency property.
|
||||
NarratorNotifier::typeid, // The owner of the dependency property.
|
||||
ref new PropertyMetadata(
|
||||
nullptr, // Default value of the dependency property.
|
||||
ref new PropertyChangedCallback(OnAnnouncementChanged)));
|
||||
}
|
||||
|
||||
void NarratorNotifier::OnAnnouncementChanged(_In_ DependencyObject^ dependencyObject, _In_ DependencyPropertyChangedEventArgs^ e)
|
||||
{
|
||||
auto instance = safe_cast<NarratorNotifier^>(dependencyObject);
|
||||
if (instance != nullptr)
|
||||
{
|
||||
instance->Announce(safe_cast<NarratorAnnouncement^>(e->NewValue));
|
||||
}
|
||||
}
|
||||
57
src/CalcViewModel/Common/Automation/NarratorNotifier.h
Normal file
57
src/CalcViewModel/Common/Automation/NarratorNotifier.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// Declaration of the NarratorNotifier class.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public ref class NarratorNotifier sealed : public Windows::UI::Xaml::DependencyObject
|
||||
{
|
||||
public:
|
||||
NarratorNotifier();
|
||||
|
||||
void Announce(NarratorAnnouncement^ announcement);
|
||||
|
||||
property NarratorAnnouncement^ Announcement
|
||||
{
|
||||
NarratorAnnouncement^ get() { return GetAnnouncement(this); }
|
||||
void set(NarratorAnnouncement^ value)
|
||||
{
|
||||
SetAnnouncement(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void RegisterDependencyProperties();
|
||||
|
||||
static property Windows::UI::Xaml::DependencyProperty^ AnnouncementProperty
|
||||
{
|
||||
Windows::UI::Xaml::DependencyProperty^ get()
|
||||
{
|
||||
return s_announcementProperty;
|
||||
}
|
||||
}
|
||||
|
||||
static NarratorAnnouncement^ GetAnnouncement(Windows::UI::Xaml::DependencyObject^ element)
|
||||
{
|
||||
return safe_cast<NarratorAnnouncement^>(element->GetValue(s_announcementProperty));
|
||||
}
|
||||
|
||||
static void SetAnnouncement(Windows::UI::Xaml::DependencyObject^ element, NarratorAnnouncement^ value)
|
||||
{
|
||||
element->SetValue(s_announcementProperty, value);
|
||||
}
|
||||
|
||||
private:
|
||||
static void OnAnnouncementChanged(
|
||||
_In_ Windows::UI::Xaml::DependencyObject^ dependencyObject,
|
||||
_In_ Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ eventArgs);
|
||||
|
||||
static Windows::UI::Xaml::DependencyProperty^ s_announcementProperty;
|
||||
|
||||
private:
|
||||
INarratorAnnouncementHost^ m_announcementHost;
|
||||
};
|
||||
}
|
||||
99
src/CalcViewModel/Common/Automation/NotificationHost.cpp
Normal file
99
src/CalcViewModel/Common/Automation/NotificationHost.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NotificationHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Windows::Foundation::Metadata;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
NotificationHost::NotificationHost() :
|
||||
m_host(nullptr)
|
||||
{}
|
||||
|
||||
bool NotificationHost::IsHostAvailable()
|
||||
{
|
||||
return ApiInformation::IsMethodPresent(
|
||||
L"Windows.UI.Xaml.Automation.Peers.AutomationPeer",
|
||||
L"RaiseNotificationEvent");
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ NotificationHost::MakeHost()
|
||||
{
|
||||
return ref new NotificationHost();
|
||||
}
|
||||
|
||||
void NotificationHost::Announce(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
if (m_host == nullptr)
|
||||
{
|
||||
m_host = ref new TextBlock();
|
||||
}
|
||||
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(m_host);
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->RaiseNotificationEvent(
|
||||
GetWindowsNotificationKind(announcement->Kind),
|
||||
GetWindowsNotificationProcessing(announcement->Processing),
|
||||
announcement->Announcement,
|
||||
announcement->ActivityId);
|
||||
}
|
||||
}
|
||||
|
||||
StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificationKind(
|
||||
CustomPeers::AutomationNotificationKind customKindType)
|
||||
{
|
||||
switch (customKindType)
|
||||
{
|
||||
case CustomPeers::AutomationNotificationKind::ItemAdded:
|
||||
return StandardPeers::AutomationNotificationKind::ItemAdded;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ItemRemoved:
|
||||
return StandardPeers::AutomationNotificationKind::ItemRemoved;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ActionCompleted:
|
||||
return StandardPeers::AutomationNotificationKind::ActionCompleted;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ActionAborted:
|
||||
return StandardPeers::AutomationNotificationKind::ActionAborted;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::Other:
|
||||
return StandardPeers::AutomationNotificationKind::Other;
|
||||
|
||||
default:
|
||||
assert(false && L"Unexpected AutomationNotificationKind");
|
||||
}
|
||||
|
||||
return StandardPeers::AutomationNotificationKind::Other;
|
||||
}
|
||||
|
||||
StandardPeers::AutomationNotificationProcessing NotificationHost::GetWindowsNotificationProcessing(
|
||||
CustomPeers::AutomationNotificationProcessing customProcessingType)
|
||||
{
|
||||
switch (customProcessingType)
|
||||
{
|
||||
case CustomPeers::AutomationNotificationProcessing::ImportantAll:
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantAll;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::ImportantMostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::All:
|
||||
return StandardPeers::AutomationNotificationProcessing::All;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::MostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::MostRecent;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::CurrentThenMostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::CurrentThenMostRecent;
|
||||
|
||||
default:
|
||||
assert(false && L"Unexpected AutomationNotificationProcessing");
|
||||
}
|
||||
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
|
||||
}
|
||||
34
src/CalcViewModel/Common/Automation/NotificationHost.h
Normal file
34
src/CalcViewModel/Common/Automation/NotificationHost.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the NotificationHost class.
|
||||
// This class announces NarratorAnnouncements using the RaiseNotification API
|
||||
// available in RS3.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public ref class NotificationHost sealed : public INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
NotificationHost();
|
||||
|
||||
virtual bool IsHostAvailable();
|
||||
virtual INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
virtual void Announce(NarratorAnnouncement^ announcement);
|
||||
|
||||
private:
|
||||
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind GetWindowsNotificationKind(
|
||||
CalculatorApp::Common::Automation::AutomationNotificationKind customKindType);
|
||||
|
||||
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing GetWindowsNotificationProcessing(
|
||||
CalculatorApp::Common::Automation::AutomationNotificationProcessing customProcessingType);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::UIElement^ m_host;
|
||||
};
|
||||
}
|
||||
|
||||
34
src/CalcViewModel/Common/BindableBase.cpp
Normal file
34
src/CalcViewModel/Common/BindableBase.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "BindableBase.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
|
||||
/// <summary>
|
||||
/// Notifies listeners that a property value has changed.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners.</param>
|
||||
void BindableBase::OnPropertyChanged(String^ propertyName)
|
||||
{
|
||||
PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Data::ICustomProperty^ BindableBase::GetCustomProperty(Platform::String^ name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Data::ICustomProperty^ BindableBase::GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Platform::String^ BindableBase::GetStringRepresentation()
|
||||
{
|
||||
return this->ToString();
|
||||
}
|
||||
35
src/CalcViewModel/Common/BindableBase.h
Normal file
35
src/CalcViewModel/Common/BindableBase.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
|
||||
{
|
||||
public:
|
||||
virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
|
||||
|
||||
public:
|
||||
// ICustomPropertyProvider
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name);
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type);
|
||||
virtual Platform::String^ GetStringRepresentation();
|
||||
|
||||
property Windows::UI::Xaml::Interop::TypeName Type
|
||||
{
|
||||
virtual Windows::UI::Xaml::Interop::TypeName get() { return this->GetType(); }
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual void OnPropertyChanged(Platform::String^ propertyName);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "CalculatorButtonPressedEventArgs.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Platform;
|
||||
|
||||
NumbersAndOperatorsEnum CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(_In_ Object^ commandParameter)
|
||||
{
|
||||
auto eventArgs = dynamic_cast<CalculatorButtonPressedEventArgs^>(commandParameter);
|
||||
if (eventArgs != nullptr)
|
||||
{
|
||||
return eventArgs->Operation;
|
||||
}
|
||||
else
|
||||
{
|
||||
return safe_cast<NumbersAndOperatorsEnum>(commandParameter);
|
||||
}
|
||||
}
|
||||
|
||||
String^ CalculatorButtonPressedEventArgs::GetAuditoryFeedbackFromCommandParameter(_In_ Object^ commandParameter)
|
||||
{
|
||||
auto eventArgs = dynamic_cast<CalculatorButtonPressedEventArgs^>(commandParameter);
|
||||
if (eventArgs != nullptr)
|
||||
{
|
||||
return eventArgs->AuditoryFeedback;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h
Normal file
24
src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
public ref class CalculatorButtonPressedEventArgs sealed
|
||||
{
|
||||
public:
|
||||
PROPERTY_R(Platform::String^, AuditoryFeedback);
|
||||
PROPERTY_R(CalculatorApp::NumbersAndOperatorsEnum, Operation);
|
||||
|
||||
CalculatorButtonPressedEventArgs(
|
||||
Platform::String^ feedback, CalculatorApp::NumbersAndOperatorsEnum operation) :
|
||||
m_AuditoryFeedback(feedback), m_Operation(operation) {}
|
||||
|
||||
static CalculatorApp::NumbersAndOperatorsEnum GetOperationFromCommandParameter(_In_ Platform::Object^ commandParameter);
|
||||
static Platform::String^ GetAuditoryFeedbackFromCommandParameter(_In_ Platform::Object^ commandParameter);
|
||||
};
|
||||
}
|
||||
}
|
||||
211
src/CalcViewModel/Common/CalculatorButtonUser.h
Normal file
211
src/CalcViewModel/Common/CalculatorButtonUser.h
Normal file
@@ -0,0 +1,211 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace CM = CalculationManager;
|
||||
|
||||
public enum class NumbersAndOperatorsEnum
|
||||
{
|
||||
Zero = (int) CM::Command::Command0,
|
||||
One = (int) CM::Command::Command1,
|
||||
Two = (int) CM::Command::Command2,
|
||||
Three = (int) CM::Command::Command3,
|
||||
Four = (int) CM::Command::Command4,
|
||||
Five = (int) CM::Command::Command5,
|
||||
Six = (int) CM::Command::Command6,
|
||||
Seven = (int) CM::Command::Command7,
|
||||
Eight = (int) CM::Command::Command8,
|
||||
Nine = (int) CM::Command::Command9,
|
||||
Add = (int) CM::Command::CommandADD,
|
||||
Subtract = (int) CM::Command::CommandSUB,
|
||||
Multiply = (int) CM::Command::CommandMUL,
|
||||
Divide = (int) CM::Command::CommandDIV,
|
||||
Invert = (int) CM::Command::CommandREC,
|
||||
Equals = (int) CM::Command::CommandEQU,
|
||||
Decimal = (int) CM::Command::CommandPNT,
|
||||
Sqrt = (int) CM::Command::CommandSQRT,
|
||||
Percent = (int) CM::Command::CommandPERCENT,
|
||||
Negate = (int) CM::Command::CommandSIGN,
|
||||
Backspace = (int) CM::Command::CommandBACK,
|
||||
ClearEntry = (int) CM::Command::CommandCENTR,
|
||||
Clear = (int) CM::Command::CommandCLEAR,
|
||||
Degree = (int) CM::Command::CommandDEG,
|
||||
Radians = (int) CM::Command::CommandRAD,
|
||||
Grads = (int) CM::Command::CommandGRAD,
|
||||
Degrees = (int) CM::Command::CommandDegrees,
|
||||
OpenParenthesis = (int) CM::Command::CommandOPENP,
|
||||
CloseParenthesis = (int) CM::Command::CommandCLOSEP,
|
||||
Pi = (int) CM::Command::CommandPI,
|
||||
Sin = (int) CM::Command::CommandSIN,
|
||||
Cos = (int) CM::Command::CommandCOS,
|
||||
Tan = (int) CM::Command::CommandTAN,
|
||||
Factorial = (int) CM::Command::CommandFAC,
|
||||
XPower2 = (int) CM::Command::CommandSQR,
|
||||
Mod = (int) CM::Command::CommandMOD,
|
||||
FToE = (int) CM::Command::CommandFE,
|
||||
LogBaseE = (int) CM::Command::CommandLN,
|
||||
InvSin = (int) CM::Command::CommandASIN,
|
||||
InvCos = (int) CM::Command::CommandACOS,
|
||||
InvTan = (int) CM::Command::CommandATAN,
|
||||
LogBase10 = (int) CM::Command::CommandLOG,
|
||||
XPowerY = (int) CM::Command::CommandPWR,
|
||||
YRootX = (int) CM::Command::CommandROOT,
|
||||
TenPowerX = (int) CM::Command::CommandPOW10,
|
||||
EPowerX = (int) CM::Command::CommandPOWE,
|
||||
Exp = (int) CM::Command::CommandEXP,
|
||||
IsScientificMode = (int) CM::Command::ModeScientific,
|
||||
IsStandardMode = (int) CM::Command::ModeBasic,
|
||||
None = (int) CM::Command::CommandNULL,
|
||||
IsProgrammerMode = (int) CM::Command::ModeProgrammer,
|
||||
DecButton = (int) CM::Command::CommandDec,
|
||||
OctButton = (int) CM::Command::CommandOct,
|
||||
HexButton = (int) CM::Command::CommandHex,
|
||||
BinButton = (int) CM::Command::CommandBin,
|
||||
And = (int) CM::Command::CommandAnd,
|
||||
Ror = (int) CM::Command::CommandROR,
|
||||
Rol = (int) CM::Command::CommandROL,
|
||||
Or = (int) CM::Command::CommandOR,
|
||||
Lsh = (int) CM::Command::CommandLSHF,
|
||||
Rsh = (int) CM::Command::CommandRSHF,
|
||||
Xor = (int) CM::Command::CommandXor,
|
||||
Not = (int) CM::Command::CommandNot,
|
||||
A = (int) CM::Command::CommandA,
|
||||
B = (int) CM::Command::CommandB,
|
||||
C = (int) CM::Command::CommandC,
|
||||
D = (int) CM::Command::CommandD,
|
||||
E = (int) CM::Command::CommandE,
|
||||
F = (int) CM::Command::CommandF,
|
||||
Memory, // This is the memory button. Doesn't have a direct mapping to the CalcEngine.
|
||||
Sinh = (int) CM::Command::CommandSINH,
|
||||
Cosh = (int) CM::Command::CommandCOSH,
|
||||
Tanh = (int) CM::Command::CommandTANH,
|
||||
InvSinh = (int) CM::Command::CommandASINH,
|
||||
InvCosh = (int) CM::Command::CommandACOSH,
|
||||
InvTanh = (int) CM::Command::CommandATANH,
|
||||
Qword = (int) CM::Command::CommandQword,
|
||||
Dword = (int) CM::Command::CommandDword,
|
||||
Word = (int) CM::Command::CommandWord,
|
||||
Byte = (int) CM::Command::CommandByte,
|
||||
Cube = (int) CM::Command::CommandCUB,
|
||||
DMS = (int) CM::Command::CommandDMS,
|
||||
|
||||
BINSTART = (int) CM::Command::CommandBINEDITSTART,
|
||||
BINPOS0 = (int) CM::Command::CommandBINPOS0,
|
||||
BINPOS1 = (int) CM::Command::CommandBINPOS1,
|
||||
BINPOS2 = (int) CM::Command::CommandBINPOS2,
|
||||
BINPOS3 = (int) CM::Command::CommandBINPOS3,
|
||||
BINPOS4 = (int) CM::Command::CommandBINPOS4,
|
||||
BINPOS5 = (int) CM::Command::CommandBINPOS5,
|
||||
BINPOS6 = (int) CM::Command::CommandBINPOS6,
|
||||
BINPOS7 = (int) CM::Command::CommandBINPOS7,
|
||||
BINPOS8 = (int) CM::Command::CommandBINPOS8,
|
||||
BINPOS9 = (int) CM::Command::CommandBINPOS9,
|
||||
BINPOS10 = (int) CM::Command::CommandBINPOS10,
|
||||
BINPOS11 = (int) CM::Command::CommandBINPOS11,
|
||||
BINPOS12 = (int) CM::Command::CommandBINPOS12,
|
||||
BINPOS13 = (int) CM::Command::CommandBINPOS13,
|
||||
BINPOS14 = (int) CM::Command::CommandBINPOS14,
|
||||
BINPOS15 = (int) CM::Command::CommandBINPOS15,
|
||||
BINPOS16 = (int) CM::Command::CommandBINPOS16,
|
||||
BINPOS17 = (int) CM::Command::CommandBINPOS17,
|
||||
BINPOS18 = (int) CM::Command::CommandBINPOS18,
|
||||
BINPOS19 = (int) CM::Command::CommandBINPOS19,
|
||||
BINPOS20 = (int) CM::Command::CommandBINPOS20,
|
||||
BINPOS21 = (int) CM::Command::CommandBINPOS21,
|
||||
BINPOS22 = (int) CM::Command::CommandBINPOS22,
|
||||
BINPOS23 = (int) CM::Command::CommandBINPOS23,
|
||||
BINPOS24 = (int) CM::Command::CommandBINPOS24,
|
||||
BINPOS25 = (int) CM::Command::CommandBINPOS25,
|
||||
BINPOS26 = (int) CM::Command::CommandBINPOS26,
|
||||
BINPOS27 = (int) CM::Command::CommandBINPOS27,
|
||||
BINPOS28 = (int) CM::Command::CommandBINPOS28,
|
||||
BINPOS29 = (int) CM::Command::CommandBINPOS29,
|
||||
BINPOS30 = (int) CM::Command::CommandBINPOS30,
|
||||
BINPOS31 = (int) CM::Command::CommandBINPOS31,
|
||||
BINPOS32 = (int) CM::Command::CommandBINPOS32,
|
||||
BINPOS33 = (int) CM::Command::CommandBINPOS33,
|
||||
BINPOS34 = (int) CM::Command::CommandBINPOS34,
|
||||
BINPOS35 = (int) CM::Command::CommandBINPOS35,
|
||||
BINPOS36 = (int) CM::Command::CommandBINPOS36,
|
||||
BINPOS37 = (int) CM::Command::CommandBINPOS37,
|
||||
BINPOS38 = (int) CM::Command::CommandBINPOS38,
|
||||
BINPOS39 = (int) CM::Command::CommandBINPOS39,
|
||||
BINPOS40 = (int) CM::Command::CommandBINPOS40,
|
||||
BINPOS41 = (int) CM::Command::CommandBINPOS41,
|
||||
BINPOS42 = (int) CM::Command::CommandBINPOS42,
|
||||
BINPOS43 = (int) CM::Command::CommandBINPOS43,
|
||||
BINPOS44 = (int) CM::Command::CommandBINPOS44,
|
||||
BINPOS45 = (int) CM::Command::CommandBINPOS45,
|
||||
BINPOS46 = (int) CM::Command::CommandBINPOS46,
|
||||
BINPOS47 = (int) CM::Command::CommandBINPOS47,
|
||||
BINPOS48 = (int) CM::Command::CommandBINPOS48,
|
||||
BINPOS49 = (int) CM::Command::CommandBINPOS49,
|
||||
BINPOS50 = (int) CM::Command::CommandBINPOS50,
|
||||
BINPOS51 = (int) CM::Command::CommandBINPOS51,
|
||||
BINPOS52 = (int) CM::Command::CommandBINPOS52,
|
||||
BINPOS53 = (int) CM::Command::CommandBINPOS53,
|
||||
BINPOS54 = (int) CM::Command::CommandBINPOS54,
|
||||
BINPOS55 = (int) CM::Command::CommandBINPOS55,
|
||||
BINPOS56 = (int) CM::Command::CommandBINPOS56,
|
||||
BINPOS57 = (int) CM::Command::CommandBINPOS57,
|
||||
BINPOS58 = (int) CM::Command::CommandBINPOS58,
|
||||
BINPOS59 = (int) CM::Command::CommandBINPOS59,
|
||||
BINPOS60 = (int) CM::Command::CommandBINPOS60,
|
||||
BINPOS61 = (int) CM::Command::CommandBINPOS61,
|
||||
BINPOS62 = (int) CM::Command::CommandBINPOS62,
|
||||
BINPOS63 = (int) CM::Command::CommandBINPOS63,
|
||||
BINEND = (int) CM::Command::CommandBINEDITEND,
|
||||
Hyp = (int) CM::Command::CommandHYP
|
||||
};
|
||||
|
||||
// This contains list of functions whose usage we are tracelogging
|
||||
public enum class FunctionLogEnum
|
||||
{
|
||||
Invert = (int) CM::Command::CommandREC,
|
||||
Sqrt = (int) CM::Command::CommandSQRT,
|
||||
Percent = (int) CM::Command::CommandPERCENT,
|
||||
Negate = (int) CM::Command::CommandSIGN,
|
||||
Degrees = (int) CM::Command::CommandDegrees,
|
||||
Pi = (int) CM::Command::CommandPI,
|
||||
Sin = (int) CM::Command::CommandSIN,
|
||||
Cos = (int) CM::Command::CommandCOS,
|
||||
Tan = (int) CM::Command::CommandTAN,
|
||||
Factorial = (int) CM::Command::CommandFAC,
|
||||
XPower2 = (int) CM::Command::CommandSQR,
|
||||
Mod = (int) CM::Command::CommandMOD,
|
||||
FToE = (int) CM::Command::CommandFE,
|
||||
LogBaseE = (int) CM::Command::CommandLN,
|
||||
InvSin = (int) CM::Command::CommandASIN,
|
||||
InvCos = (int) CM::Command::CommandACOS,
|
||||
InvTan = (int) CM::Command::CommandATAN,
|
||||
LogBase10 = (int) CM::Command::CommandLOG,
|
||||
XPowerY = (int) CM::Command::CommandPWR,
|
||||
YRootX = (int) CM::Command::CommandROOT,
|
||||
TenPowerX = (int) CM::Command::CommandPOW10,
|
||||
EPowerX = (int) CM::Command::CommandPOWE,
|
||||
Exp = (int) CM::Command::CommandEXP,
|
||||
DecButton = (int) CM::Command::CommandDec,
|
||||
OctButton = (int) CM::Command::CommandOct,
|
||||
HexButton = (int) CM::Command::CommandHex,
|
||||
BinButton = (int) CM::Command::CommandBin,
|
||||
And = (int) CM::Command::CommandAnd,
|
||||
Ror = (int) CM::Command::CommandROR,
|
||||
Rol = (int) CM::Command::CommandROL,
|
||||
Or = (int) CM::Command::CommandOR,
|
||||
Lsh = (int) CM::Command::CommandLSHF,
|
||||
Rsh = (int) CM::Command::CommandRSHF,
|
||||
Xor = (int) CM::Command::CommandXor,
|
||||
Not = (int) CM::Command::CommandNot,
|
||||
Sinh = (int) CM::Command::CommandSINH,
|
||||
Cosh = (int) CM::Command::CommandCOSH,
|
||||
Tanh = (int) CM::Command::CommandTANH,
|
||||
InvSinh = (int) CM::Command::CommandASINH,
|
||||
InvCosh = (int) CM::Command::CommandACOSH,
|
||||
InvTanh = (int) CM::Command::CommandATANH,
|
||||
Cube = (int) CM::Command::CommandCUB,
|
||||
DMS = (int) CM::Command::CommandDMS,
|
||||
};
|
||||
}
|
||||
134
src/CalcViewModel/Common/CalculatorDisplay.cpp
Normal file
134
src/CalcViewModel/Common/CalculatorDisplay.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// This class provides the concrete implemenation for the ICalcDisplay interface
|
||||
// that is declared in the Calculation Manager Library.
|
||||
#include "pch.h"
|
||||
#include "CalculatorDisplay.h"
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculationManager;
|
||||
using namespace std;
|
||||
|
||||
CalculatorDisplay::CalculatorDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetCallback(Platform::WeakReference callbackReference)
|
||||
{
|
||||
m_callbackReference = callbackReference;
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetHistoryCallback(Platform::WeakReference callbackReference)
|
||||
{
|
||||
m_historyCallbackReference = callbackReference;
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetPrimaryDisplay(_In_ const wstring& displayStringValue, _In_ bool isError)
|
||||
{
|
||||
if (m_callbackReference)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetPrimaryDisplay(displayStringValue, isError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetParenDisplayText(_In_ const std::wstring& parenthesisCount)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetParenthesisCount(parenthesisCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetIsInError(bool isError)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->IsInError = isError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetExpressionDisplay(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens, _Inout_ std::shared_ptr<CalculatorVector <std::shared_ptr<IExpressionCommand>>> const &commands)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetExpressionDisplay(tokens, commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetMemorizedNumbers(_In_ const vector<std::wstring>& newMemorizedNumbers)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetMemorizedNumbers(newMemorizedNumbers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::OnHistoryItemAdded(_In_ unsigned int addedItemIndex)
|
||||
{
|
||||
if (m_historyCallbackReference != nullptr)
|
||||
{
|
||||
auto historyVM = m_historyCallbackReference.Resolve<ViewModel::HistoryViewModel>();
|
||||
if (historyVM)
|
||||
{
|
||||
historyVM->OnHistoryItemAdded(addedItemIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::MaxDigitsReached()
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->OnMaxDigitsReached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::BinaryOperatorReceived()
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->OnBinaryOperatorReceived();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::MemoryItemChanged(unsigned int indexOfMemory)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->OnMemoryItemChanged(indexOfMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/CalcViewModel/Common/CalculatorDisplay.h
Normal file
31
src/CalcViewModel/Common/CalculatorDisplay.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
// Callback interface to be implemented by the CalculatorManager
|
||||
class CalculatorDisplay: public ICalcDisplay
|
||||
{
|
||||
public:
|
||||
CalculatorDisplay();
|
||||
void SetCallback(Platform::WeakReference callbackReference);
|
||||
void SetHistoryCallback(Platform::WeakReference callbackReference);
|
||||
|
||||
private:
|
||||
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(_In_ const std::wstring& parenthesisCount) override;
|
||||
void MaxDigitsReached() override;
|
||||
void BinaryOperatorReceived() override;
|
||||
void MemoryItemChanged(unsigned int indexOfMemory) override;
|
||||
|
||||
private:
|
||||
Platform::WeakReference m_callbackReference;
|
||||
Platform::WeakReference m_historyCallbackReference;
|
||||
};
|
||||
}
|
||||
40
src/CalcViewModel/Common/ConversionResultTaskHelper.cpp
Normal file
40
src/CalcViewModel/Common/ConversionResultTaskHelper.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ConversionResultTaskHelper.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace concurrency;
|
||||
using namespace std;
|
||||
|
||||
ConversionResultTaskHelper::ConversionResultTaskHelper(unsigned int delay, const function<void()> functionToRun) :
|
||||
m_delay{ delay },
|
||||
m_storedFunction{ functionToRun }
|
||||
{
|
||||
auto token = m_cts.get_token();
|
||||
auto delayTask = CompleteAfter(delay);
|
||||
delayTask.then([this, token]()
|
||||
{
|
||||
if (!token.is_canceled())
|
||||
{
|
||||
m_storedFunction();
|
||||
}
|
||||
}, task_continuation_context::use_current());
|
||||
}
|
||||
|
||||
ConversionResultTaskHelper::~ConversionResultTaskHelper()
|
||||
{
|
||||
m_cts.cancel();
|
||||
}
|
||||
|
||||
#pragma optimize("", off)
|
||||
// Creates a task that completes after the specified delay.
|
||||
//
|
||||
// Taken from: How to: Create a Task that Completes After a Delay
|
||||
// https://msdn.microsoft.com/en-us/library/hh873170.aspx
|
||||
task<void> ConversionResultTaskHelper::CompleteAfter(unsigned int timeout)
|
||||
{
|
||||
co_await winrt::resume_after(winrt::Windows::Foundation::TimeSpan{ std::chrono::duration<uint32_t, std::milli>(timeout) });
|
||||
};
|
||||
#pragma optimize("", on)
|
||||
24
src/CalcViewModel/Common/ConversionResultTaskHelper.h
Normal file
24
src/CalcViewModel/Common/ConversionResultTaskHelper.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class ConversionResultTaskHelper
|
||||
{
|
||||
public:
|
||||
ConversionResultTaskHelper(unsigned int delay, const std::function<void()> functionToRun);
|
||||
~ConversionResultTaskHelper();
|
||||
|
||||
private:
|
||||
concurrency::task<void> CompleteAfter(unsigned int timeout);
|
||||
|
||||
unsigned int m_delay;
|
||||
concurrency::cancellation_token_source m_cts;
|
||||
const std::function<void()> m_storedFunction;
|
||||
};
|
||||
}
|
||||
}
|
||||
569
src/CalcViewModel/Common/CopyPasteManager.cpp
Normal file
569
src/CalcViewModel/Common/CopyPasteManager.cpp
Normal file
@@ -0,0 +1,569 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "CopyPasteManager.h"
|
||||
#include "Common\LocalizationSettings.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace concurrency;
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::System;
|
||||
using namespace Windows::ApplicationModel::DataTransfer;
|
||||
|
||||
size_t maxOperandLength;
|
||||
unsigned long long maxOperandNumber;
|
||||
|
||||
String^ CopyPasteManager::supportedFormats[] =
|
||||
{
|
||||
StandardDataFormats::Text
|
||||
};
|
||||
|
||||
constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" };
|
||||
// [\s\x85] means white-space characters
|
||||
static const wstring c_wspc = L"[\\s\\x85]*";
|
||||
static const wstring c_wspcLParens = c_wspc + L"[(]*" + c_wspc;
|
||||
static const wstring c_wspcRParens = c_wspc + L"[)]*" + c_wspc;
|
||||
static const wstring c_signedDecFloat = L"[-+]?\\d*(\\d|[.])\\d*";
|
||||
|
||||
// Programmer Mode Integer patterns
|
||||
// Support digit separators ` (WinDbg/MASM), ' (C++), and _ (C# and other languages)
|
||||
static const wstring c_hexProgrammerChars = L"([a-f]|[A-F]|\\d)+((_|'|`)([a-f]|[A-F]|\\d)+)*";
|
||||
static const wstring c_decProgrammerChars = L"\\d+((_|'|`)\\d+)*";
|
||||
static const wstring c_octProgrammerChars = L"[0-7]+((_|'|`)[0-7]+)*";
|
||||
static const wstring c_binProgrammerChars = L"[0-1]+((_|'|`)[0-1]+)*";
|
||||
static const wstring c_uIntSuffixes = L"[uU]?[lL]{0,2}";
|
||||
|
||||
// RegEx Patterns used by various modes
|
||||
static const array<wregex, 1> standardModePatterns =
|
||||
{
|
||||
wregex(c_wspc + c_signedDecFloat + c_wspc)
|
||||
};
|
||||
static const array<wregex, 2> scientificModePatterns =
|
||||
{
|
||||
wregex(c_wspcLParens + c_signedDecFloat + c_wspcRParens),
|
||||
wregex(c_wspcLParens + c_signedDecFloat + L"[e]([+]|[-])+\\d+" + c_wspcRParens)
|
||||
};
|
||||
static const array<array<wregex, 5>, 4> programmerModePatterns =
|
||||
{ {
|
||||
// Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh
|
||||
{
|
||||
wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens),
|
||||
wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens)
|
||||
},
|
||||
// Decimal numbers like -145, 145, 0n145, 123ull etc
|
||||
{
|
||||
wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" +c_wspcRParens),
|
||||
wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens)
|
||||
},
|
||||
// Octal numbers like 06, 010, 0t77, 0o77, 077ull etc
|
||||
{
|
||||
wregex(c_wspcLParens + L"(0[otOT])?" + c_octProgrammerChars + c_uIntSuffixes + c_wspcRParens)
|
||||
},
|
||||
// Binary numbers like 011010110, 0010110, 10101001, 1001b, 0b1001, 0y1001, 0b1001ull
|
||||
{
|
||||
wregex(c_wspcLParens + L"(0[byBY])?" + c_binProgrammerChars + c_uIntSuffixes + c_wspcRParens),
|
||||
wregex(c_wspcLParens + c_binProgrammerChars + L"[bB]?" + c_wspcRParens)
|
||||
}
|
||||
} };
|
||||
static const array<wregex, 1> unitConverterPatterns =
|
||||
{
|
||||
wregex(c_wspc + L"[-+]?\\d*[.]?\\d*" + c_wspc)
|
||||
};
|
||||
|
||||
void CopyPasteManager::CopyToClipboard(String^ stringToCopy)
|
||||
{
|
||||
// Copy the string to the clipboard
|
||||
auto dataPackage = ref new DataPackage();
|
||||
dataPackage->SetText(stringToCopy);
|
||||
Clipboard::SetContent(dataPackage);
|
||||
}
|
||||
|
||||
task<String^> CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
// Retrieve the text in the clipboard
|
||||
auto dataPackageView = Clipboard::GetContent();
|
||||
|
||||
// TODO: Suport all formats supported by ClipboardHasText
|
||||
//-- add support to avoid pasting of expressions like 12 34(as of now we allow 1234)
|
||||
//-- add support to allow pasting for expressions like .2 , -.2
|
||||
//-- add support to allow pasting for expressions like 1.3e12(as of now we allow 1.3e+12)
|
||||
|
||||
return create_task((dataPackageView->GetTextAsync(::StandardDataFormats::Text)))
|
||||
.then([mode, modeType, programmerNumberBase, bitLengthType](String^ pastedText)
|
||||
{
|
||||
return ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType);
|
||||
}
|
||||
, task_continuation_context::use_arbitrary());
|
||||
}
|
||||
|
||||
int CopyPasteManager::ClipboardTextFormat()
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
auto dataPackageView = Clipboard::GetContent();
|
||||
|
||||
for (int i = 0; i < RTL_NUMBER_OF(supportedFormats); i++)
|
||||
{
|
||||
if (dataPackageView->Contains(supportedFormats[i]))
|
||||
{
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
return CopyPasteManager::ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType);
|
||||
}
|
||||
|
||||
// return "NoOp" if pastedText is invalid else return pastedText
|
||||
|
||||
String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
if (pastedText->Length() > MaxPasteableLength)
|
||||
{
|
||||
// return NoOp to indicate don't paste anything.
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"PastedExpressionSizeGreaterThanMaxAllowed", L"MoreThanMaxInput", mode, programmerNumberBase, bitLengthType);
|
||||
return StringReference(PasteErrorString);
|
||||
}
|
||||
|
||||
wstring pasteExpression = pastedText->Data();
|
||||
|
||||
// Get english translated expression
|
||||
String^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(pasteExpression);
|
||||
|
||||
// Removing the spaces, comma separator from the pasteExpression to allow pasting of expressions like 1 + 2+1,333
|
||||
pasteExpression = Utils::RemoveUnwantedCharsFromWstring(englishString->Data());
|
||||
|
||||
// If the last character is an = sign, remove it from the pasteExpression to allow evaluating the result on paste.
|
||||
if (!pasteExpression.empty() && pasteExpression.back() == L'=')
|
||||
{
|
||||
pasteExpression = pasteExpression.substr(0, pasteExpression.length() - 1);
|
||||
}
|
||||
|
||||
// Extract operands from the expression to make regex comparison easy and quick. For whole expression it was taking too much of time.
|
||||
// Operands vector will have the list of operands in the pasteExpression
|
||||
vector<wstring> operands = ExtractOperands(pasteExpression, mode, programmerNumberBase, bitLengthType);
|
||||
if (operands.empty())
|
||||
{
|
||||
// return NoOp to indicate don't paste anything.
|
||||
return StringReference(PasteErrorString);
|
||||
}
|
||||
|
||||
if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
operands = { pasteExpression };
|
||||
}
|
||||
|
||||
// validate each operand with patterns for different modes
|
||||
if (!ExpressionRegExMatch(operands, mode, modeType, programmerNumberBase, bitLengthType))
|
||||
{
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"InvalidExpressionForPresentMode", pastedText->Data(), mode, programmerNumberBase, bitLengthType);
|
||||
return StringReference(PasteErrorString);
|
||||
}
|
||||
|
||||
return ref new String(pastedText->Data());
|
||||
}
|
||||
|
||||
vector<wstring> CopyPasteManager::ExtractOperands(const wstring& pasteExpression, ViewMode mode, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
vector<wstring> operands{};
|
||||
size_t lastIndex = 0;
|
||||
bool haveOperator = false;
|
||||
bool startExpCounting = false;
|
||||
bool startOfExpression = true;
|
||||
bool isPreviousOpenParen = false;
|
||||
bool isPreviousOperator = false;
|
||||
|
||||
// This will have the exponent length
|
||||
size_t expLength = 0;
|
||||
for (size_t i = 0; i < pasteExpression.length(); i++)
|
||||
{
|
||||
// if the current character is not a valid one don't process it
|
||||
if (c_validCharacterSet.find(pasteExpression.at(i)) == wstring_view::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operands.size() >= MaxOperandCount)
|
||||
{
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"OperandCountGreaterThanMaxCount", pasteExpression.c_str(), mode, programmerNumberBase, bitLengthType);
|
||||
operands.clear();
|
||||
return operands;
|
||||
}
|
||||
|
||||
if (startExpCounting)
|
||||
{
|
||||
if ((pasteExpression.at(i) >= L'0') && (pasteExpression.at(i) <= L'9'))
|
||||
{
|
||||
expLength++;
|
||||
|
||||
// to disallow pasting of 1e+12345 as 1e+1234, max exponent that can be pasted is 9999.
|
||||
if (expLength > MaxExponentLength)
|
||||
{
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"ExponentLengthGreaterThanMaxLength", pasteExpression.c_str(), mode, programmerNumberBase, bitLengthType);
|
||||
operands.clear();
|
||||
return operands;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((mode != ViewMode::Programmer) && (pasteExpression.at(i) == L'e'))
|
||||
{
|
||||
startExpCounting = true;
|
||||
}
|
||||
|
||||
if (((pasteExpression.at(i) == L'+') || (pasteExpression.at(i) == L'-') || (pasteExpression.at(i) == L'*') || (pasteExpression.at(i) == L'/')))
|
||||
{
|
||||
if ((pasteExpression.at(i) == L'+') || (pasteExpression.at(i) == L'-'))
|
||||
{
|
||||
// don't break the expression into operands if the encountered character corresponds to sign command(+-)
|
||||
if (isPreviousOpenParen || startOfExpression || isPreviousOperator || ((mode != ViewMode::Programmer) && !((i != 0) && (pasteExpression.at(i - 1) != L'e'))))
|
||||
{
|
||||
isPreviousOperator = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
startExpCounting = false;
|
||||
expLength = 0;
|
||||
haveOperator = true;
|
||||
isPreviousOperator = true;
|
||||
operands.push_back(pasteExpression.substr(lastIndex, i - lastIndex));
|
||||
lastIndex = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
isPreviousOperator = false;
|
||||
}
|
||||
|
||||
isPreviousOpenParen = (pasteExpression.at(i) == L'(');
|
||||
startOfExpression = false;
|
||||
}
|
||||
|
||||
if (!haveOperator)
|
||||
{
|
||||
operands.clear();
|
||||
operands.push_back(pasteExpression);
|
||||
}
|
||||
else
|
||||
{
|
||||
operands.push_back(pasteExpression.substr(lastIndex, pasteExpression.length() - 1));
|
||||
}
|
||||
|
||||
return operands;
|
||||
}
|
||||
|
||||
bool CopyPasteManager::ExpressionRegExMatch(vector<wstring> operands, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
if (operands.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expMatched = true;
|
||||
vector<wregex> patterns{};
|
||||
|
||||
pair<size_t, uint64_t> operandLimits = GetMaxOperandLengthAndValue(mode, modeType, programmerNumberBase, bitLengthType);
|
||||
size_t maxOperandLength = operandLimits.first;
|
||||
uint64_t maxOperandValue = operandLimits.second;
|
||||
|
||||
if (mode == ViewMode::Standard)
|
||||
{
|
||||
patterns.assign(standardModePatterns.begin(), standardModePatterns.end());
|
||||
}
|
||||
else if (mode == ViewMode::Scientific)
|
||||
{
|
||||
patterns.assign(scientificModePatterns.begin(), scientificModePatterns.end());
|
||||
}
|
||||
else if (mode == ViewMode::Programmer)
|
||||
{
|
||||
patterns.assign(programmerModePatterns[programmerNumberBase - HexBase].begin(), programmerModePatterns[programmerNumberBase - HexBase].end());
|
||||
}
|
||||
else if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
patterns.assign(unitConverterPatterns.begin(), unitConverterPatterns.end());
|
||||
}
|
||||
|
||||
for (const wstring& operand : operands)
|
||||
{
|
||||
// Each operand only needs to match one of the available patterns.
|
||||
bool operandMatched = false;
|
||||
for (const wregex& pattern : patterns)
|
||||
{
|
||||
operandMatched = operandMatched || regex_match(operand, pattern);
|
||||
}
|
||||
|
||||
if (operandMatched)
|
||||
{
|
||||
// Remove characters that are valid in the expression but we do not want to include in length calculations
|
||||
// or which will break conversion from string-to-ULL.
|
||||
wstring operandValue = SanitizeOperand(operand);
|
||||
|
||||
// If an operand exceeds the maximum length allowed, break and return.
|
||||
if (OperandLength(operandValue, mode, modeType, programmerNumberBase) > maxOperandLength)
|
||||
{
|
||||
expMatched = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If maxOperandValue is set and the operandValue exceeds it, break and return.
|
||||
if (maxOperandValue != 0)
|
||||
{
|
||||
unsigned long long int operandAsULL = 0;
|
||||
if (!TryOperandToULL(operandValue, programmerNumberBase, operandAsULL))
|
||||
{
|
||||
// Operand was empty, received invalid_argument, or received out_of_range. Input is invalid.
|
||||
expMatched = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (operandAsULL > maxOperandValue)
|
||||
{
|
||||
expMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expMatched = expMatched && operandMatched;
|
||||
}
|
||||
|
||||
return expMatched;
|
||||
}
|
||||
|
||||
pair<size_t, uint64_t> CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
size_t maxLength = 0;
|
||||
uint64_t maxValue = 0;
|
||||
|
||||
if (mode == ViewMode::Standard)
|
||||
{
|
||||
maxLength = MaxStandardOperandLength;
|
||||
}
|
||||
else if (mode == ViewMode::Scientific)
|
||||
{
|
||||
maxLength = MaxScientificOperandLength;
|
||||
}
|
||||
else if (mode == ViewMode::Programmer)
|
||||
{
|
||||
unsigned int bitLength = 0;
|
||||
switch (bitLengthType)
|
||||
{
|
||||
case QwordType:
|
||||
bitLength = 64;
|
||||
break;
|
||||
case DwordType:
|
||||
bitLength = 32;
|
||||
break;
|
||||
case WordType:
|
||||
bitLength = 16;
|
||||
break;
|
||||
case ByteType:
|
||||
bitLength = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
double bitsPerDigit = 0;
|
||||
switch (programmerNumberBase)
|
||||
{
|
||||
case BinBase:
|
||||
bitsPerDigit = log2(2);
|
||||
break;
|
||||
case OctBase:
|
||||
bitsPerDigit = log2(8);
|
||||
break;
|
||||
case DecBase:
|
||||
bitsPerDigit = log2(10);
|
||||
break;
|
||||
case HexBase:
|
||||
bitsPerDigit = log2(16);
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int signBit = (programmerNumberBase == DecBase) ? 1 : 0;
|
||||
|
||||
maxLength = (size_t)ceil((bitLength - signBit) / bitsPerDigit);
|
||||
maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit));
|
||||
}
|
||||
else if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
maxLength = MaxConverterInputLength;
|
||||
}
|
||||
|
||||
return make_pair(maxLength, maxValue);
|
||||
}
|
||||
|
||||
wstring CopyPasteManager::SanitizeOperand(const wstring& operand)
|
||||
{
|
||||
wchar_t unWantedChars[] = { L'\'', L'_', L'`', L'(', L')', L'-' };
|
||||
|
||||
return Utils::RemoveUnwantedCharsFromWstring(operand, unWantedChars, ARRAYSIZE(unWantedChars));
|
||||
}
|
||||
|
||||
bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, unsigned long long int& result)
|
||||
{
|
||||
result = 0;
|
||||
|
||||
if (operand.length() == 0 || operand.front() == L'-')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default to base10
|
||||
int intBase = 10;
|
||||
switch (numberBase)
|
||||
{
|
||||
case HexBase:
|
||||
intBase = 16;
|
||||
break;
|
||||
case OctBase:
|
||||
intBase = 8;
|
||||
break;
|
||||
case BinBase:
|
||||
intBase = 2;
|
||||
break;
|
||||
case DecBase:
|
||||
intBase = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
wstring::size_type size = 0;
|
||||
try
|
||||
{
|
||||
result = stoull(operand, &size, intBase);
|
||||
return true;
|
||||
}
|
||||
catch (invalid_argument)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
catch (out_of_range)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t CopyPasteManager::OperandLength(wstring operand, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase)
|
||||
{
|
||||
size_t len = 0;
|
||||
if (mode == ViewMode::Standard || mode == ViewMode::Scientific)
|
||||
{
|
||||
len = StandardScientificOperandLength(operand);
|
||||
}
|
||||
else if (mode == ViewMode::Programmer)
|
||||
{
|
||||
len = ProgrammerOperandLength(operand, programmerNumberBase);
|
||||
}
|
||||
else if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
len = operand.length();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t CopyPasteManager::StandardScientificOperandLength(wstring operand)
|
||||
{
|
||||
bool hasDecimal = false;
|
||||
for (size_t i = 0; i < operand.length(); i++)
|
||||
{
|
||||
if (operand[i] == L'.')
|
||||
{
|
||||
hasDecimal = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDecimal)
|
||||
{
|
||||
if (operand.length() >= 2)
|
||||
{
|
||||
if ((operand[0] == L'0') && (operand[1] == L'.'))
|
||||
{
|
||||
return operand.length() - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return operand.length() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operand.length();
|
||||
}
|
||||
|
||||
size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int numberBase)
|
||||
{
|
||||
size_t len = operand.length();
|
||||
|
||||
vector<wstring> prefixes{};
|
||||
vector<wstring> suffixes{};
|
||||
switch (numberBase)
|
||||
{
|
||||
case BinBase:
|
||||
prefixes = { L"0B", L"0Y" };
|
||||
suffixes = { L"B" };
|
||||
break;
|
||||
case DecBase:
|
||||
prefixes = { L"-", L"0N" };
|
||||
break;
|
||||
case OctBase:
|
||||
prefixes = { L"0T", L"0O" };
|
||||
break;
|
||||
case HexBase:
|
||||
prefixes = { L"0X" };
|
||||
suffixes = { L"H" };
|
||||
break;
|
||||
default:
|
||||
// No defined prefixes/suffixes
|
||||
break;
|
||||
}
|
||||
|
||||
// UInt suffixes are common across all modes
|
||||
const array<wstring, 5> uintSuffixes = { L"ULL", L"UL", L"LL", L"U", L"L" };
|
||||
suffixes.insert(suffixes.end(), uintSuffixes.begin(), uintSuffixes.end());
|
||||
|
||||
wstring operandUpper = operand;
|
||||
transform(operandUpper.begin(), operandUpper.end(), operandUpper.begin(), toupper);
|
||||
|
||||
// Detect if there is a suffix and subtract its length
|
||||
// Check suffixes first to allow e.g. "0b" to result in length 1 (value 0), rather than length 0 (no value).
|
||||
for (const wstring& suffix : suffixes)
|
||||
{
|
||||
if (len < suffix.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operandUpper.compare(operandUpper.length() - suffix.length(), suffix.length(), suffix) == 0)
|
||||
{
|
||||
len -= suffix.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect if there is a prefix and subtract its length
|
||||
for (const wstring& prefix : prefixes)
|
||||
{
|
||||
if (len < prefix.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operandUpper.compare(0, prefix.length(), prefix) == 0)
|
||||
{
|
||||
len -= prefix.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
72
src/CalcViewModel/Common/CopyPasteManager.h
Normal file
72
src/CalcViewModel/Common/CopyPasteManager.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "AppResourceProvider.h"
|
||||
|
||||
namespace CalculatorUnitTests
|
||||
{
|
||||
class CopyPasteManagerTest;
|
||||
}
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
|
||||
#define QwordType 1
|
||||
#define DwordType 2
|
||||
#define WordType 3
|
||||
#define ByteType 4
|
||||
#define HexBase 5
|
||||
#define DecBase 6
|
||||
#define OctBase 7
|
||||
#define BinBase 8
|
||||
|
||||
class CopyPasteManager
|
||||
{
|
||||
public:
|
||||
static void CopyToClipboard(Platform::String^ stringToCopy);
|
||||
static concurrency::task<Platform::String^> GetStringToPaste(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
static bool HasStringToPaste()
|
||||
{
|
||||
return ClipboardTextFormat() >= 0;
|
||||
}
|
||||
|
||||
static constexpr auto PasteErrorString = L"NoOp";
|
||||
|
||||
private:
|
||||
static int ClipboardTextFormat();
|
||||
static Platform::String^ ValidatePasteExpression(
|
||||
Platform::String^ pastedText,
|
||||
CalculatorApp::Common::ViewMode mode,
|
||||
int programmerNumberBase,
|
||||
int bitLengthType);
|
||||
static Platform::String^ ValidatePasteExpression(
|
||||
Platform::String^ pastedText,
|
||||
CalculatorApp::Common::ViewMode mode,
|
||||
CalculatorApp::Common::CategoryGroupType modeType,
|
||||
int programmerNumberBase,
|
||||
int bitLengthType);
|
||||
|
||||
static std::vector<std::wstring> ExtractOperands(const std::wstring& pasteExpression, CalculatorApp::Common::ViewMode mode, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
static bool ExpressionRegExMatch(std::vector<std::wstring> operands, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
|
||||
static std::pair<size_t, uint64_t> GetMaxOperandLengthAndValue(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
static std::wstring SanitizeOperand(const std::wstring& operand);
|
||||
static bool TryOperandToULL(const std::wstring& operand, int numberBase, unsigned long long int& result);
|
||||
static size_t OperandLength(std::wstring operand, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1);
|
||||
static size_t StandardScientificOperandLength(std::wstring operand);
|
||||
static size_t ProgrammerOperandLength(const std::wstring& operand, int numberBase);
|
||||
|
||||
static constexpr size_t MaxStandardOperandLength = 16;
|
||||
static constexpr size_t MaxScientificOperandLength = 32;
|
||||
static constexpr size_t MaxConverterInputLength = 16;
|
||||
static constexpr size_t MaxOperandCount = 100;
|
||||
static constexpr size_t MaxPasteableLength = 512;
|
||||
static constexpr size_t MaxExponentLength = 4;
|
||||
static constexpr size_t MaxProgrammerBitLength = 64;
|
||||
|
||||
static Platform::String^ supportedFormats[];
|
||||
|
||||
friend class CalculatorUnitTests::CopyPasteManagerTest;
|
||||
};
|
||||
}
|
||||
292
src/CalcViewModel/Common/DateCalculator.cpp
Normal file
292
src/CalcViewModel/Common/DateCalculator.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "DateCalculator.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace CalculatorApp::Common::DateCalculation;
|
||||
|
||||
DateCalculationEngine::DateCalculationEngine(_In_ String^ calendarIdentifier)
|
||||
{
|
||||
m_calendar = ref new Calendar();
|
||||
m_calendar->ChangeCalendarSystem(calendarIdentifier);
|
||||
}
|
||||
|
||||
// Adding Duration to a Date
|
||||
// Returns: True if function succeeds to calculate the date else returns False
|
||||
bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime *endDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_calendar->SetDateTime(startDate);
|
||||
|
||||
if (duration.year != 0)
|
||||
{
|
||||
m_calendar->AddYears(duration.year);
|
||||
}
|
||||
if (duration.month != 0)
|
||||
{
|
||||
m_calendar->AddMonths(duration.month);
|
||||
}
|
||||
if (duration.day != 0)
|
||||
{
|
||||
m_calendar->AddDays(duration.day);
|
||||
}
|
||||
|
||||
*endDate = m_calendar->GetDateTime();
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^ ex)
|
||||
{
|
||||
// Do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Subtracting Duration from a Date
|
||||
// Returns: True if function succeeds to calculate the date else returns False
|
||||
bool DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime *endDate)
|
||||
{
|
||||
// For Subtract the Algorithm is different than Add. Here the smaller units are subtracted first
|
||||
// and then the larger units.
|
||||
try
|
||||
{
|
||||
m_calendar->SetDateTime(startDate);
|
||||
|
||||
if (duration.day != 0)
|
||||
{
|
||||
m_calendar->AddDays(-duration.day);
|
||||
}
|
||||
if (duration.month != 0)
|
||||
{
|
||||
m_calendar->AddMonths(-duration.month);
|
||||
}
|
||||
if (duration.year != 0)
|
||||
{
|
||||
m_calendar->AddYears(-duration.year);
|
||||
}
|
||||
|
||||
*endDate = m_calendar->GetDateTime();
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^ ex)
|
||||
{
|
||||
// Do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the UniversalTime value is not negative
|
||||
return (endDate->UniversalTime >= 0);
|
||||
}
|
||||
|
||||
// Calculate the difference between two dates
|
||||
void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime date2, _In_ DateUnit outputFormat, _Out_ DateDifference *difference)
|
||||
{
|
||||
DateTime startDate;
|
||||
DateTime endDate;
|
||||
DateTime pivotDate;
|
||||
DateTime tempPivotDate;
|
||||
UINT daysDiff = 0;
|
||||
UINT differenceInDates[c_unitsOfDate] = { 0 };
|
||||
|
||||
if (date1.UniversalTime < date2.UniversalTime)
|
||||
{
|
||||
startDate = date1;
|
||||
endDate = date2;
|
||||
}
|
||||
else
|
||||
{
|
||||
startDate = date2;
|
||||
endDate = date1;
|
||||
}
|
||||
|
||||
pivotDate = startDate;
|
||||
|
||||
daysDiff = GetDifferenceInDays(startDate, endDate);
|
||||
|
||||
// If output has units other than days
|
||||
// 0th bit: Year, 1st bit: Month, 2nd bit: Week, 3rd bit: Day
|
||||
if (static_cast<int>(outputFormat) & 7)
|
||||
{
|
||||
UINT daysInMonth;
|
||||
UINT approximateDaysInYear;
|
||||
|
||||
// If we're unable to calculate the days-in-month or days-in-year, we'll leave the values at 0.
|
||||
if (TryGetCalendarDaysInMonth(startDate, daysInMonth)
|
||||
&& TryGetCalendarDaysInYear(endDate, approximateDaysInYear))
|
||||
{
|
||||
UINT daysIn[c_unitsOfDate] = { approximateDaysInYear, daysInMonth, c_daysInWeek, 1 };
|
||||
|
||||
for (int unitIndex = 0; unitIndex < c_unitsGreaterThanDays; unitIndex++)
|
||||
{
|
||||
tempPivotDate = pivotDate;
|
||||
|
||||
// Check if the bit flag is set for the date unit
|
||||
DateUnit dateUnit = static_cast<DateUnit>(1 << unitIndex);
|
||||
|
||||
if (static_cast<int>(outputFormat & dateUnit))
|
||||
{
|
||||
bool isEndDateHit = false;
|
||||
differenceInDates[unitIndex] = (daysDiff / daysIn[unitIndex]);
|
||||
|
||||
if (differenceInDates[unitIndex] != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast<int>(differenceInDates[unitIndex]));
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^)
|
||||
{
|
||||
// Operation failed due to out of bound result
|
||||
// Do nothing
|
||||
differenceInDates[unitIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int tempDaysDiff;
|
||||
|
||||
do
|
||||
{
|
||||
tempDaysDiff = GetDifferenceInDays(pivotDate, endDate);
|
||||
|
||||
if (tempDaysDiff < 0)
|
||||
{
|
||||
// pivotDate has gone over the end date; start from the begining of this unit
|
||||
differenceInDates[unitIndex] -= 1;
|
||||
pivotDate = tempPivotDate;
|
||||
pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast<int>(differenceInDates[unitIndex]));
|
||||
isEndDateHit = true;
|
||||
}
|
||||
else if (tempDaysDiff > 0)
|
||||
{
|
||||
if (isEndDateHit)
|
||||
{
|
||||
// This is the closest the pivot can get to the end date for this unit
|
||||
break;
|
||||
}
|
||||
|
||||
// pivotDate is still below the end date
|
||||
try
|
||||
{
|
||||
pivotDate = AdjustCalendarDate(pivotDate, dateUnit, 1);
|
||||
differenceInDates[unitIndex] += 1;
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^)
|
||||
{
|
||||
// handling for 31st Dec, 9999 last valid date
|
||||
// Do nothing - break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (tempDaysDiff != 0); // dates are the same - exit the loop
|
||||
|
||||
tempPivotDate = AdjustCalendarDate(tempPivotDate, dateUnit, static_cast<int>(differenceInDates[unitIndex]));
|
||||
pivotDate = tempPivotDate;
|
||||
daysDiff = GetDifferenceInDays(pivotDate, endDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
differenceInDates[3] = daysDiff;
|
||||
|
||||
difference->year = differenceInDates[0];
|
||||
difference->month = differenceInDates[1];
|
||||
difference->week = differenceInDates[2];
|
||||
difference->day = differenceInDates[3];
|
||||
}
|
||||
|
||||
|
||||
// Private Methods
|
||||
|
||||
// Gets number of days between the two date time values
|
||||
int DateCalculationEngine::GetDifferenceInDays(DateTime date1, DateTime date2)
|
||||
{
|
||||
// A tick is defined as the number of 100 nanoseconds
|
||||
long long ticksDifference = date2.UniversalTime - date1.UniversalTime;
|
||||
return static_cast<int>(ticksDifference / static_cast<long long>(c_day));
|
||||
}
|
||||
|
||||
// Gets number of Calendar days in the month in which this date falls.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DateCalculationEngine::TryGetCalendarDaysInMonth(_In_ DateTime date, _Out_ UINT& daysInMonth)
|
||||
{
|
||||
bool result = false;
|
||||
m_calendar->SetDateTime(date);
|
||||
|
||||
// NumberOfDaysInThisMonth returns -1 if unknown.
|
||||
int daysInThisMonth = m_calendar->NumberOfDaysInThisMonth;
|
||||
if (daysInThisMonth != -1)
|
||||
{
|
||||
daysInMonth = static_cast<UINT>(daysInThisMonth);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Gets number of Calendar days in the year in which this date falls.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DateCalculationEngine::TryGetCalendarDaysInYear(_In_ DateTime date, _Out_ UINT& daysInYear)
|
||||
{
|
||||
bool result = false;
|
||||
UINT days = 0;
|
||||
|
||||
m_calendar->SetDateTime(date);
|
||||
|
||||
// NumberOfMonthsInThisYear returns -1 if unknown.
|
||||
int monthsInYear = m_calendar->NumberOfMonthsInThisYear;
|
||||
if (monthsInYear != -1)
|
||||
{
|
||||
bool monthResult = true;
|
||||
|
||||
// Not all years begin with Month 1.
|
||||
int firstMonthThisYear = m_calendar->FirstMonthInThisYear;
|
||||
for (int month = 0; month < monthsInYear; month++)
|
||||
{
|
||||
m_calendar->Month = firstMonthThisYear + month;
|
||||
|
||||
// NumberOfDaysInThisMonth returns -1 if unknown.
|
||||
int daysInMonth = m_calendar->NumberOfDaysInThisMonth;
|
||||
if (daysInMonth == -1)
|
||||
{
|
||||
monthResult = false;
|
||||
break;
|
||||
}
|
||||
|
||||
days += daysInMonth;
|
||||
}
|
||||
|
||||
if (monthResult)
|
||||
{
|
||||
daysInYear = days;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Adds/Subtracts certain value for a particular date unit
|
||||
DateTime DateCalculationEngine::AdjustCalendarDate(Windows::Foundation::DateTime date, DateUnit dateUnit, int difference)
|
||||
{
|
||||
m_calendar->SetDateTime(date);
|
||||
|
||||
switch (dateUnit)
|
||||
{
|
||||
case DateUnit::Year:
|
||||
m_calendar->AddYears(difference);
|
||||
break;
|
||||
case DateUnit::Month:
|
||||
m_calendar->AddMonths(difference);
|
||||
break;
|
||||
case DateUnit::Week:
|
||||
m_calendar->AddWeeks(difference);
|
||||
break;
|
||||
}
|
||||
|
||||
return m_calendar->GetDateTime();
|
||||
}
|
||||
62
src/CalcViewModel/Common/DateCalculator.h
Normal file
62
src/CalcViewModel/Common/DateCalculator.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
const ULONGLONG c_millisecond = 10000;
|
||||
const ULONGLONG c_second = 1000 * c_millisecond;
|
||||
const ULONGLONG c_minute = 60 * c_second;
|
||||
const ULONGLONG c_hour = 60 * c_minute;
|
||||
const ULONGLONG c_day = 24 * c_hour;
|
||||
|
||||
const int c_unitsOfDate = 4; // Units Year,Month,Week,Day
|
||||
const int c_unitsGreaterThanDays = 3; // Units Greater than Days (Year/Month/Week) 3
|
||||
const int c_daysInWeek = 7;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace DateCalculation
|
||||
{
|
||||
public enum class _Enum_is_bitflag_ DateUnit
|
||||
{
|
||||
Year = 0x01,
|
||||
Month = 0x02,
|
||||
Week = 0x04,
|
||||
Day = 0x08
|
||||
};
|
||||
|
||||
// Struct to store the difference between two Dates in the form of Years, Months , Weeks
|
||||
struct DateDifference
|
||||
{
|
||||
int year = 0;
|
||||
int month = 0;
|
||||
int week = 0;
|
||||
int day = 0;
|
||||
};
|
||||
|
||||
class DateCalculationEngine
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
DateCalculationEngine(_In_ Platform::String^ calendarIdentifier);
|
||||
|
||||
// Public Methods
|
||||
bool __nothrow AddDuration(_In_ Windows::Foundation::DateTime startDate, _In_ const DateDifference& duration, _Out_ Windows::Foundation::DateTime *endDate);
|
||||
bool __nothrow SubtractDuration(_In_ Windows::Foundation::DateTime startDate, _In_ const DateDifference& duration, _Out_ Windows::Foundation::DateTime *endDate);
|
||||
void __nothrow GetDateDifference(_In_ Windows::Foundation::DateTime date1, _In_ Windows::Foundation::DateTime date2, _In_ DateUnit outputFormat, _Out_ DateDifference *difference);
|
||||
|
||||
private:
|
||||
// Private Variables
|
||||
Windows::Globalization::Calendar^ m_calendar;
|
||||
|
||||
// Private Methods
|
||||
int GetDifferenceInDays(Windows::Foundation::DateTime date1, Windows::Foundation::DateTime date2);
|
||||
bool TryGetCalendarDaysInMonth(_In_ Windows::Foundation::DateTime date, _Out_ UINT& daysInMonth);
|
||||
bool TryGetCalendarDaysInYear(_In_ Windows::Foundation::DateTime date, _Out_ UINT& daysInYear);
|
||||
Windows::Foundation::DateTime AdjustCalendarDate(Windows::Foundation::DateTime date, DateUnit dateUnit, int difference);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/CalcViewModel/Common/DelegateCommand.h
Normal file
70
src/CalcViewModel/Common/DelegateCommand.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
template <typename TTarget>
|
||||
ref class DelegateCommand: public Windows::UI::Xaml::Input::ICommand
|
||||
{
|
||||
internal:
|
||||
|
||||
typedef void (TTarget::*CommandHandlerFunc)(Platform::Object^);
|
||||
|
||||
DelegateCommand(TTarget^ target, CommandHandlerFunc func):
|
||||
m_weakTarget(target),
|
||||
m_function(func)
|
||||
{ }
|
||||
|
||||
private:
|
||||
|
||||
// Explicit, and private, implementation of ICommand, this way of programming makes it so
|
||||
// the ICommand methods will only be available if the ICommand interface is requested via a dynamic_cast
|
||||
// The ICommand interface is meant to be consumed by Xaml and not by the app, this is a defensive measure against
|
||||
// code in the app calling Execute.
|
||||
virtual void ExecuteImpl(Platform::Object^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::Execute
|
||||
{
|
||||
TTarget^ target = m_weakTarget.Resolve<TTarget>();
|
||||
if (target)
|
||||
{
|
||||
(target->*m_function)(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool CanExecuteImpl(Platform::Object^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::CanExecute
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual event Windows::Foundation::EventHandler<Platform::Object^>^ CanExecuteChangedImpl
|
||||
{
|
||||
virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::EventHandler<Platform::Object^>^ handler) sealed = Windows::UI::Xaml::Input::ICommand::CanExecuteChanged::add
|
||||
{
|
||||
return m_canExecuteChanged += handler;
|
||||
}
|
||||
virtual void remove(Windows::Foundation::EventRegistrationToken token) sealed = Windows::UI::Xaml::Input::ICommand::CanExecuteChanged::remove
|
||||
{
|
||||
m_canExecuteChanged -= token;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
event Windows::Foundation::EventHandler<Platform::Object^>^ m_canExecuteChanged;
|
||||
|
||||
CommandHandlerFunc m_function;
|
||||
Platform::WeakReference m_weakTarget;
|
||||
|
||||
};
|
||||
|
||||
template <typename TTarget, typename TFuncPtr>
|
||||
DelegateCommand<TTarget>^ MakeDelegate(TTarget^ target, TFuncPtr&& function)
|
||||
{
|
||||
return ref new DelegateCommand<TTarget>(target, std::forward<TFuncPtr>(function));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
46
src/CalcViewModel/Common/DisplayExpressionToken.h
Normal file
46
src/CalcViewModel/Common/DisplayExpressionToken.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp::Common
|
||||
{
|
||||
public enum class TokenType
|
||||
{
|
||||
Operator,
|
||||
Operand,
|
||||
Separator
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class DisplayExpressionToken sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
internal:
|
||||
DisplayExpressionToken(Platform::String^ token, int tokenPosition, bool fEditable, TokenType type) :
|
||||
m_Token(token), m_TokenPosition(tokenPosition), m_IsTokenEditable(fEditable), m_Type(type), m_OriginalToken(token), m_InEditMode(false)
|
||||
{}
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Token);
|
||||
OBSERVABLE_PROPERTY_RW(int, TokenPosition);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsTokenEditable);
|
||||
OBSERVABLE_PROPERTY_RW(int, CommandIndex);
|
||||
OBSERVABLE_PROPERTY_R(Platform::String^, OriginalToken);
|
||||
|
||||
property bool IsTokenInEditMode {
|
||||
bool get() { return m_InEditMode; }
|
||||
void set(bool val)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
m_OriginalToken = ref new Platform::String(m_Token->Data());
|
||||
}
|
||||
m_InEditMode = val;
|
||||
}
|
||||
}
|
||||
internal:
|
||||
OBSERVABLE_PROPERTY_RW(TokenType, Type);
|
||||
private:
|
||||
bool m_InEditMode;
|
||||
};
|
||||
}
|
||||
50
src/CalcViewModel/Common/EngineResourceProvider.cpp
Normal file
50
src/CalcViewModel/Common/EngineResourceProvider.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "EngineResourceProvider.h"
|
||||
#include "Common/LocalizationSettings.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Platform;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace std;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
EngineResourceProvider::EngineResourceProvider()
|
||||
{
|
||||
m_resLoader = ResourceLoader::GetForViewIndependentUse("CEngineStrings");
|
||||
}
|
||||
|
||||
wstring EngineResourceProvider::GetCEngineString(const wstring& id)
|
||||
{
|
||||
const auto& localizationSettings = LocalizationSettings::GetInstance();
|
||||
|
||||
if (id.compare(L"sDecimal") == 0)
|
||||
{
|
||||
return localizationSettings.GetDecimalSeparatorStr();
|
||||
}
|
||||
|
||||
if (id.compare(L"sThousand") == 0)
|
||||
{
|
||||
return localizationSettings.GetNumberGroupingSeparatorStr();
|
||||
}
|
||||
|
||||
if (id.compare(L"sGrouping") == 0)
|
||||
{
|
||||
// The following groupings are the onces that CalcEngine supports.
|
||||
// 0;0 0x000 - no grouping
|
||||
// 3;0 0x003 - group every 3 digits
|
||||
// 3;2;0 0x023 - group 1st 3 and then every 2 digits
|
||||
// 4;0 0x004 - group every 4 digits
|
||||
// 5;3;2;0 0x235 - group 5, then 3, then every 2
|
||||
wstring numberGroupingString = localizationSettings.GetNumberGroupingStr();
|
||||
return numberGroupingString;
|
||||
}
|
||||
|
||||
StringReference idRef(id.c_str());
|
||||
String^ str = m_resLoader->GetString(idRef);
|
||||
return str->Begin();
|
||||
}
|
||||
}
|
||||
17
src/CalcViewModel/Common/EngineResourceProvider.h
Normal file
17
src/CalcViewModel/Common/EngineResourceProvider.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
class EngineResourceProvider : public CalculationManager::IResourceProvider
|
||||
{
|
||||
public:
|
||||
EngineResourceProvider();
|
||||
virtual std::wstring GetCEngineString(const std::wstring& id) override;
|
||||
|
||||
private:
|
||||
Windows::ApplicationModel::Resources::ResourceLoader^ m_resLoader;
|
||||
};
|
||||
}
|
||||
87
src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp
Normal file
87
src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ExpressionCommandDeserializer.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Windows::Storage::Streams;
|
||||
|
||||
CommandDeserializer::CommandDeserializer(_In_ DataReader^ dataReader) :m_dataReader(dataReader){}
|
||||
|
||||
std::shared_ptr<IExpressionCommand> CommandDeserializer::Deserialize(_In_ CalculationManager::CommandType cmdType)
|
||||
{
|
||||
switch (cmdType)
|
||||
{
|
||||
case CalculationManager::CommandType::OperandCommand:
|
||||
|
||||
return std::make_shared<COpndCommand>(DeserializeOperand());
|
||||
break;
|
||||
|
||||
case CalculationManager::CommandType::Parentheses:
|
||||
|
||||
return std::make_shared<CParentheses>(DeserializeParentheses());
|
||||
break;
|
||||
|
||||
case CalculationManager::CommandType::UnaryCommand:
|
||||
|
||||
return std::make_shared<CUnaryCommand>(DeserializeUnary());
|
||||
break;
|
||||
|
||||
case CalculationManager::CommandType::BinaryCommand:
|
||||
|
||||
return std::make_shared<CBinaryCommand>(DeserializeBinary());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ref new Platform::Exception(E_INVALIDARG, ref new Platform::String(L"Unknown command type"));
|
||||
}
|
||||
}
|
||||
|
||||
COpndCommand CommandDeserializer::DeserializeOperand()
|
||||
{
|
||||
bool fNegative = m_dataReader->ReadBoolean();
|
||||
bool fDecimal = m_dataReader->ReadBoolean();
|
||||
bool fSciFmt = m_dataReader->ReadBoolean();
|
||||
|
||||
std::shared_ptr<CalculatorVector<int>> cmdVector = std::make_shared<CalculatorVector<int>>();
|
||||
auto cmdVectorSize = m_dataReader->ReadUInt32();
|
||||
|
||||
for (unsigned int j = 0; j < cmdVectorSize; ++j)
|
||||
{
|
||||
int eachOpndcmd = m_dataReader->ReadInt32();
|
||||
cmdVector->Append(eachOpndcmd);
|
||||
}
|
||||
|
||||
return COpndCommand(cmdVector, fNegative, fDecimal, fSciFmt);
|
||||
}
|
||||
|
||||
CParentheses CommandDeserializer::DeserializeParentheses()
|
||||
{
|
||||
int parenthesisCmd = m_dataReader->ReadInt32();
|
||||
return CParentheses(parenthesisCmd);
|
||||
}
|
||||
|
||||
CUnaryCommand CommandDeserializer::DeserializeUnary()
|
||||
{
|
||||
auto cmdSize = m_dataReader->ReadUInt32();
|
||||
std::shared_ptr<CalculatorVector<int>> cmdVector = std::make_shared<CalculatorVector<int>>();
|
||||
|
||||
if (cmdSize == 1)
|
||||
{
|
||||
int eachOpndcmd = m_dataReader->ReadInt32();
|
||||
return CUnaryCommand(eachOpndcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
int eachOpndcmd1 = m_dataReader->ReadInt32();
|
||||
int eachOpndcmd2 = m_dataReader->ReadInt32();
|
||||
return CUnaryCommand(eachOpndcmd1, eachOpndcmd2);
|
||||
}
|
||||
}
|
||||
|
||||
CBinaryCommand CommandDeserializer::DeserializeBinary()
|
||||
{
|
||||
int cmd = m_dataReader->ReadInt32();
|
||||
return CBinaryCommand(cmd);
|
||||
}
|
||||
24
src/CalcViewModel/Common/ExpressionCommandDeserializer.h
Normal file
24
src/CalcViewModel/Common/ExpressionCommandDeserializer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class CommandDeserializer
|
||||
{
|
||||
public:
|
||||
CommandDeserializer(_In_ Windows::Storage::Streams::DataReader^ dataReader);
|
||||
std::shared_ptr<IExpressionCommand> Deserialize(_In_ CalculationManager::CommandType cmdType);
|
||||
|
||||
private:
|
||||
Windows::Storage::Streams::DataReader^ m_dataReader;
|
||||
COpndCommand DeserializeOperand();
|
||||
CParentheses DeserializeParentheses();
|
||||
CUnaryCommand DeserializeUnary();
|
||||
CBinaryCommand DeserializeBinary();
|
||||
};
|
||||
}
|
||||
}
|
||||
56
src/CalcViewModel/Common/ExpressionCommandSerializer.cpp
Normal file
56
src/CalcViewModel/Common/ExpressionCommandSerializer.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Common\ExpressionCommandSerializer.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Windows::Storage::Streams;
|
||||
|
||||
SerializeCommandVisitor::SerializeCommandVisitor(_In_ DataWriter^ dataWriter) :m_dataWriter(dataWriter)
|
||||
{
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ COpndCommand &opndCmd)
|
||||
{
|
||||
m_dataWriter->WriteBoolean(opndCmd.IsNegative());
|
||||
m_dataWriter->WriteBoolean(opndCmd.IsDecimalPresent());
|
||||
m_dataWriter->WriteBoolean(opndCmd.IsSciFmt());
|
||||
|
||||
auto opndCmds = opndCmd.GetCommands();
|
||||
unsigned int opndCmdSize;
|
||||
opndCmds->GetSize(&opndCmdSize);
|
||||
m_dataWriter->WriteUInt32(opndCmdSize);
|
||||
for (unsigned int j = 0; j < opndCmdSize; ++j)
|
||||
{
|
||||
int eachOpndcmd;
|
||||
opndCmds->GetAt(j, &eachOpndcmd);
|
||||
m_dataWriter->WriteInt32(eachOpndcmd);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ CUnaryCommand &unaryCmd)
|
||||
{
|
||||
auto cmds = unaryCmd.GetCommands();
|
||||
unsigned int cmdSize;
|
||||
cmds->GetSize(&cmdSize);
|
||||
m_dataWriter->WriteUInt32(cmdSize);
|
||||
for (unsigned int j = 0; j < cmdSize; ++j)
|
||||
{
|
||||
int eachOpndcmd;
|
||||
cmds->GetAt(j, &eachOpndcmd);
|
||||
m_dataWriter->WriteInt32(eachOpndcmd);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ CBinaryCommand &binaryCmd)
|
||||
{
|
||||
int cmd = binaryCmd.GetCommand();
|
||||
m_dataWriter->WriteInt32(cmd);
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ CParentheses ¶Cmd)
|
||||
{
|
||||
int parenthesisCmd = paraCmd.GetCommand();
|
||||
m_dataWriter->WriteInt32(parenthesisCmd);
|
||||
}
|
||||
24
src/CalcViewModel/Common/ExpressionCommandSerializer.h
Normal file
24
src/CalcViewModel/Common/ExpressionCommandSerializer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class SerializeCommandVisitor : public ISerializeCommandVisitor
|
||||
{
|
||||
public:
|
||||
SerializeCommandVisitor(_In_ Windows::Storage::Streams::DataWriter^ dataWriter);
|
||||
|
||||
void Visit(_In_ COpndCommand &opndCmd);
|
||||
void Visit(_In_ CUnaryCommand &unaryCmd);
|
||||
void Visit(_In_ CBinaryCommand &binaryCmd);
|
||||
void Visit(_In_ CParentheses ¶Cmd);
|
||||
|
||||
private:
|
||||
Windows::Storage::Streams::DataWriter^ m_dataWriter;
|
||||
};
|
||||
}
|
||||
}
|
||||
876
src/CalcViewModel/Common/KeyboardShortcutManager.cpp
Normal file
876
src/CalcViewModel/Common/KeyboardShortcutManager.cpp
Normal file
@@ -0,0 +1,876 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "KeyboardShortcutManager.h"
|
||||
#include "AppResourceProvider.h"
|
||||
#include "ApplicationViewModel.h"
|
||||
#include "LocalizationSettings.h"
|
||||
|
||||
using namespace Concurrency;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
using namespace Windows::System;
|
||||
using namespace Utils;
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
|
||||
namespace MUXC = Microsoft::UI::Xaml::Controls;
|
||||
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, Character);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKey);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyShiftChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyAltChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlShiftChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyInverseChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlInverseChord);
|
||||
|
||||
static multimap<int, multimap<wchar_t, WeakReference>> s_CharacterForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeysForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyShiftChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyAltChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlShiftChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyInverseChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlInverseChordsForButtons;
|
||||
|
||||
static const TimeSpan c_lightUpTime = { 500000 }; // Quarter of a second
|
||||
static multimap<int, bool> s_ShiftKeyPressed;
|
||||
static multimap<int, bool> s_ControlKeyPressed;
|
||||
static multimap<int, bool> s_ShiftButtonChecked;
|
||||
static multimap<int, bool> s_IsDropDownOpen;
|
||||
|
||||
static reader_writer_lock s_keyboardShortcutMapLock;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
// Lights up all of the buttons in the given range
|
||||
// The range is defined by a pair of iterators
|
||||
template <typename T>
|
||||
void LightUpButtons(const T& buttons)
|
||||
{
|
||||
auto iterator = buttons.first;
|
||||
for (; iterator != buttons.second; ++iterator)
|
||||
{
|
||||
auto button = iterator->second.Resolve<ButtonBase>();
|
||||
if (button && button->IsEnabled)
|
||||
{
|
||||
LightUpButton(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LightUpButton(ButtonBase^ button)
|
||||
{
|
||||
// If the button is a toggle button then we don't need
|
||||
// to change the UI of the button
|
||||
if (dynamic_cast<ToggleButton^>(button))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The button will go into the visual Pressed state with this call
|
||||
VisualStateManager::GoToState(button, "Pressed", true);
|
||||
|
||||
// This timer will fire after c_lightUpTime and make the button
|
||||
// go back to the normal state.
|
||||
// This timer will only fire once after which it will be destroyed
|
||||
auto timer = ref new DispatcherTimer();
|
||||
timer->Interval = c_lightUpTime;
|
||||
|
||||
WeakReference timerWeakReference(timer);
|
||||
WeakReference buttonWeakReference(button);
|
||||
timer->Tick += ref new EventHandler<Object^>(
|
||||
[buttonWeakReference, timerWeakReference](Object^, Object^)
|
||||
{
|
||||
auto button = buttonWeakReference.Resolve<ButtonBase>();
|
||||
if (button)
|
||||
{
|
||||
VisualStateManager::GoToState(button, "Normal", true);
|
||||
}
|
||||
|
||||
// Cancel the timer after we're done so it only fires once
|
||||
auto timer = timerWeakReference.Resolve<DispatcherTimer>();
|
||||
if (timer)
|
||||
{
|
||||
timer->Stop();
|
||||
}
|
||||
});
|
||||
timer->Start();
|
||||
}
|
||||
|
||||
// Looks for the first button reference that it can resolve
|
||||
// and execute its command.
|
||||
// NOTE: It is assumed that all buttons associated with a particular
|
||||
// key have the same command
|
||||
template <typename T>
|
||||
void RunFirstEnabledButtonCommand(const T& buttons)
|
||||
{
|
||||
auto buttonIterator = buttons.first;
|
||||
for (; buttonIterator != buttons.second; ++buttonIterator)
|
||||
{
|
||||
auto button = buttonIterator->second.Resolve<ButtonBase>();
|
||||
if (button && button->IsEnabled)
|
||||
{
|
||||
RunButtonCommand(button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunButtonCommand(ButtonBase^ button)
|
||||
{
|
||||
if (button->IsEnabled)
|
||||
{
|
||||
auto command = button->Command;
|
||||
auto parameter = button->CommandParameter;
|
||||
if (command && command->CanExecute(parameter))
|
||||
{
|
||||
command->Execute(parameter);
|
||||
}
|
||||
|
||||
auto radio = dynamic_cast<RadioButton^>(button);
|
||||
if (radio)
|
||||
{
|
||||
radio->IsChecked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto toggle = dynamic_cast<ToggleButton^>(button);
|
||||
if (toggle)
|
||||
{
|
||||
toggle->IsChecked = !toggle->IsChecked->Value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static multimap<int, bool> s_ignoreNextEscape;
|
||||
static multimap<int, bool> s_keepIgnoringEscape;
|
||||
static multimap<int, bool> s_fHonorShortcuts;
|
||||
static multimap<int, bool> s_fHandledEnter;
|
||||
static multimap<int, Flyout^> s_AboutFlyout;
|
||||
|
||||
void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
||||
{
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_ignoreNextEscape.insert(std::make_pair(viewId, true));
|
||||
}
|
||||
|
||||
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
||||
{
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.insert(std::make_pair(viewId, !onlyOnce));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::HonorEscape()
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
||||
{
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_ignoreNextEscape.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
|
||||
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
||||
{
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnCharacterPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
String^ oldValue,
|
||||
String^ newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_CharacterForButtons.find(viewId);
|
||||
|
||||
if (iterViewMap != s_CharacterForButtons.end())
|
||||
{
|
||||
if (oldValue)
|
||||
{
|
||||
iterViewMap->second.erase(oldValue->Data()[0]);
|
||||
}
|
||||
|
||||
if (newValue)
|
||||
{
|
||||
if (newValue == L".")
|
||||
{
|
||||
wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator();
|
||||
iterViewMap->second.insert(std::make_pair(decSep, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s_CharacterForButtons.insert(std::make_pair(viewId, std::multimap<wchar_t, WeakReference>()));
|
||||
|
||||
if (newValue == L".")
|
||||
{
|
||||
wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator();
|
||||
s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(decSep, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = static_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeysForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeysForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeysForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeysForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyControlChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
Control^ control = dynamic_cast<ButtonBase^>(target);
|
||||
|
||||
if (control == nullptr)
|
||||
{
|
||||
// Handling Ctrl+E shortcut for Date Calc, target would be NavigationView^ in that case
|
||||
control = safe_cast<MUXC::NavigationView^>(target);
|
||||
}
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyControlChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyControlChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(control)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyControlChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyControlChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(control)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyShiftChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyShiftChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyShiftChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyAltChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
MUXC::NavigationView^ navView = safe_cast<MUXC::NavigationView^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyAltChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyAltChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(navView)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyAltChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyAltChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(navView)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyControlShiftChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyControlShiftChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyControlShiftChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyInverseChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyInverseChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyInverseChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyControlInverseChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
// In the three event handlers bellow we will not mark the event as handled
|
||||
// because this is a sumplemental operation and we don't want to interfere with
|
||||
// the normal keyboard handling.
|
||||
void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow^ sender, CharacterReceivedEventArgs^ args)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId);
|
||||
|
||||
if (currentHonorShortcuts != s_fHonorShortcuts.end())
|
||||
{
|
||||
if (currentHonorShortcuts->second)
|
||||
{
|
||||
wchar_t character = static_cast<wchar_t>(args->KeyCode);
|
||||
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);
|
||||
|
||||
RunFirstEnabledButtonCommand(buttons);
|
||||
|
||||
LightUpButtons(buttons);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::multimap<MyVirtualKey, WeakReference>& GetCurrentKeyDictionary(MyVirtualKey key, bool altPressed = false)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (altPressed)
|
||||
{
|
||||
return s_VirtualKeyAltChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else if ((s_ShiftKeyPressed.find(viewId)->second) && ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down))
|
||||
{
|
||||
return s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else if (s_ShiftKeyPressed.find(viewId)->second)
|
||||
{
|
||||
return s_VirtualKeyShiftChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else if (s_ShiftButtonChecked.find(viewId)->second)
|
||||
{
|
||||
if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)
|
||||
{
|
||||
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
||||
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
|
||||
{
|
||||
if (key == iterator->first)
|
||||
{
|
||||
return s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
||||
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
|
||||
{
|
||||
if (key == iterator->first)
|
||||
{
|
||||
return s_VirtualKeyInverseChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)
|
||||
{
|
||||
return s_VirtualKeyControlChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return s_VirtualKeysForButtons.find(viewId)->second;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^ args)
|
||||
{
|
||||
// If keyboard shortcuts like Ctrl+C or Ctrl+V are not handled
|
||||
if (!args->Handled)
|
||||
{
|
||||
auto key = args->VirtualKey;
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
||||
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
||||
|
||||
bool isControlKeyPressed = (currentControlKeyPressed != s_ControlKeyPressed.end()) && (currentControlKeyPressed->second);
|
||||
bool isShiftKeyPressed = (currentShiftKeyPressed != s_ShiftKeyPressed.end()) && (currentShiftKeyPressed->second);
|
||||
|
||||
// Handle Ctrl + E for DateCalculator
|
||||
if ((key == VirtualKey::E) &&
|
||||
isControlKeyPressed &&
|
||||
!isShiftKeyPressed)
|
||||
{
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
|
||||
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
||||
auto navView = buttons.first->second.Resolve<MUXC::NavigationView>();
|
||||
auto appViewModel = safe_cast<ApplicationViewModel^>(navView->DataContext);
|
||||
appViewModel->Mode = ViewMode::Date;
|
||||
auto categoryName = AppResourceProvider::GetInstance().GetResourceString(L"DateCalculationModeText");
|
||||
appViewModel->CategoryName = categoryName;
|
||||
|
||||
auto menuItems = static_cast<IObservableVector<Object^>^>(navView->MenuItemsSource);
|
||||
auto flatIndex = NavCategory::GetFlatIndex(ViewMode::Date);
|
||||
navView->SelectedItem = menuItems->GetAt(flatIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId);
|
||||
|
||||
auto currentIgnoreNextEscape = s_ignoreNextEscape.find(viewId);
|
||||
|
||||
if (currentIgnoreNextEscape != s_ignoreNextEscape.end())
|
||||
{
|
||||
if (currentIgnoreNextEscape->second && key == VirtualKey::Escape)
|
||||
{
|
||||
auto currentKeepIgnoringEscape = s_keepIgnoringEscape.find(viewId);
|
||||
|
||||
if (currentKeepIgnoringEscape != s_keepIgnoringEscape.end())
|
||||
{
|
||||
if (!currentKeepIgnoringEscape->second)
|
||||
{
|
||||
HonorEscape();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key == VirtualKey::Control)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
||||
|
||||
if (currentControlKeyPressed != s_ControlKeyPressed.end())
|
||||
{
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.insert(std::make_pair(viewId, true));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (key == VirtualKey::Shift)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
||||
|
||||
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
|
||||
{
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ShiftKeyPressed.insert(std::make_pair(viewId, true));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
|
||||
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
||||
|
||||
auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId);
|
||||
|
||||
if (currentHonorShortcuts != s_fHonorShortcuts.end())
|
||||
{
|
||||
if (currentHonorShortcuts->second)
|
||||
{
|
||||
RunFirstEnabledButtonCommand(buttons);
|
||||
|
||||
// Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V is pressed.
|
||||
// When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open.
|
||||
// Ctrl+Insert is equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V
|
||||
if (currentIsDropDownOpen != s_IsDropDownOpen.end() && !currentIsDropDownOpen->second)
|
||||
{
|
||||
// Do not Light Up Buttons when Ctrl+C, Ctrl+V, Ctrl+Insert or Shift+Insert is pressed
|
||||
if (!(isControlKeyPressed && (key == VirtualKey::C || key == VirtualKey::V || key == VirtualKey::Insert))
|
||||
&& !(isShiftKeyPressed && (key == VirtualKey::Insert)))
|
||||
{
|
||||
LightUpButtons(buttons);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow^ sender, KeyEventArgs^ args)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto key = args->VirtualKey;
|
||||
|
||||
if (args->VirtualKey == VirtualKey::Shift)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
||||
|
||||
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
|
||||
{
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ShiftKeyPressed.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
}
|
||||
else if (args->VirtualKey == VirtualKey::Control)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
||||
|
||||
if (currentControlKeyPressed != s_ControlKeyPressed.end())
|
||||
{
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnAcceleratorKeyActivated(CoreDispatcher^, AcceleratorKeyEventArgs^ args)
|
||||
{
|
||||
if (args->KeyStatus.IsKeyReleased)
|
||||
{
|
||||
auto key = args->VirtualKey;
|
||||
bool altPressed = args->KeyStatus.IsMenuKeyDown;
|
||||
|
||||
// If the Alt/Menu key is not pressed then we don't care about the key anymore
|
||||
if (!altPressed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key), altPressed);
|
||||
auto listItems = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
||||
for (auto listIterator = listItems.first; listIterator != listItems.second; ++listIterator)
|
||||
{
|
||||
auto item = listIterator->second.Resolve<MUXC::NavigationView>();
|
||||
if (item != nullptr)
|
||||
{
|
||||
auto navView = safe_cast<MUXC::NavigationView^> (item);
|
||||
|
||||
auto menuItems = static_cast<IObservableVector<Object^>^>(navView->MenuItemsSource);
|
||||
if (menuItems != nullptr)
|
||||
{
|
||||
auto vm = safe_cast<ApplicationViewModel^>(navView->DataContext);
|
||||
if (nullptr != vm)
|
||||
{
|
||||
ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast<MyVirtualKey>(key));
|
||||
if (NavCategory::IsValidViewMode(toMode))
|
||||
{
|
||||
vm->Mode = toMode;
|
||||
navView->SelectedItem = menuItems->GetAt(NavCategory::GetFlatIndex(toMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args->VirtualKey == VirtualKey::Escape)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_AboutFlyout.find(viewId);
|
||||
|
||||
if ((iterViewMap != s_AboutFlyout.end()) && (iterViewMap->second != nullptr))
|
||||
{
|
||||
iterViewMap->second->Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::Initialize()
|
||||
{
|
||||
auto coreWindow = Window::Current->CoreWindow;
|
||||
coreWindow->CharacterReceived +=
|
||||
ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(&KeyboardShortcutManager::OnCharacterReceivedHandler);
|
||||
coreWindow->KeyDown +=
|
||||
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(&KeyboardShortcutManager::OnKeyDownHandler);
|
||||
coreWindow->KeyUp +=
|
||||
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(&KeyboardShortcutManager::OnKeyUpHandler);
|
||||
coreWindow->Dispatcher->AcceleratorKeyActivated +=
|
||||
ref new TypedEventHandler<CoreDispatcher^, AcceleratorKeyEventArgs^>(&KeyboardShortcutManager::OnAcceleratorKeyActivated);
|
||||
|
||||
KeyboardShortcutManager::RegisterNewAppViewId();
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::ShiftButtonChecked(bool checked)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_ShiftButtonChecked.find(viewId) != s_ShiftButtonChecked.end())
|
||||
{
|
||||
s_ShiftButtonChecked.erase(viewId);
|
||||
s_ShiftButtonChecked.insert(std::make_pair(viewId, checked));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::UpdateDropDownState(bool isOpen)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end())
|
||||
{
|
||||
s_IsDropDownOpen.erase(viewId);
|
||||
s_IsDropDownOpen.insert(std::make_pair(viewId, isOpen));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::UpdateDropDownState(Flyout^ aboutPageFlyout)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_AboutFlyout.find(viewId) != s_AboutFlyout.end())
|
||||
{
|
||||
s_AboutFlyout.erase(viewId);
|
||||
s_AboutFlyout.insert(std::make_pair(viewId, aboutPageFlyout));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::HonorShortcuts(bool allow)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_fHonorShortcuts.find(viewId) != s_fHonorShortcuts.end())
|
||||
{
|
||||
s_fHonorShortcuts.erase(viewId);
|
||||
s_fHonorShortcuts.insert(std::make_pair(viewId, allow));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::HandledEnter(bool ishandled)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_fHandledEnter.find(viewId) != s_fHandledEnter.end())
|
||||
{
|
||||
s_fHandledEnter.erase(viewId);
|
||||
s_fHandledEnter.insert(std::make_pair(viewId, ishandled));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::RegisterNewAppViewId()
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int appViewId = Utils::GetWindowId();
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (s_CharacterForButtons.find(appViewId) == s_CharacterForButtons.end())
|
||||
{
|
||||
s_CharacterForButtons.insert(std::make_pair(appViewId, std::multimap<wchar_t, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeysForButtons.find(appViewId) == s_VirtualKeysForButtons.end())
|
||||
{
|
||||
s_VirtualKeysForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyControlChordsForButtons.find(appViewId) == s_VirtualKeyControlChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyControlChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyShiftChordsForButtons.find(appViewId) == s_VirtualKeyShiftChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyAltChordsForButtons.find(appViewId) == s_VirtualKeyAltChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyAltChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyControlShiftChordsForButtons.find(appViewId) == s_VirtualKeyControlShiftChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyInverseChordsForButtons.find(appViewId) == s_VirtualKeyInverseChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyControlInverseChordsForButtons.find(appViewId) == s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
s_ShiftKeyPressed.insert(std::make_pair(appViewId, false));
|
||||
s_ControlKeyPressed.insert(std::make_pair(appViewId, false));
|
||||
s_ShiftButtonChecked.insert(std::make_pair(appViewId, false));
|
||||
s_IsDropDownOpen.insert(std::make_pair(appViewId, false));
|
||||
s_ignoreNextEscape.insert(std::make_pair(appViewId, false));
|
||||
s_keepIgnoringEscape.insert(std::make_pair(appViewId, false));
|
||||
s_fHonorShortcuts.insert(std::make_pair(appViewId, true));
|
||||
s_fHandledEnter.insert(std::make_pair(appViewId, true));
|
||||
s_AboutFlyout.insert(std::make_pair(appViewId, nullptr));
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnWindowClosed(int viewId)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
s_CharacterForButtons.erase(viewId);
|
||||
|
||||
s_VirtualKeysForButtons.erase(viewId);
|
||||
s_VirtualKeyControlChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyShiftChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyAltChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyControlShiftChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyInverseChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyControlInverseChordsForButtons.erase(viewId);
|
||||
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ShiftButtonChecked.erase(viewId);
|
||||
s_IsDropDownOpen.erase(viewId);
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_fHonorShortcuts.erase(viewId);
|
||||
s_fHandledEnter.erase(viewId);
|
||||
s_AboutFlyout.erase(viewId);
|
||||
}
|
||||
95
src/CalcViewModel/Common/KeyboardShortcutManager.h
Normal file
95
src/CalcViewModel/Common/KeyboardShortcutManager.h
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
public ref class KeyboardShortcutManager sealed
|
||||
{
|
||||
public:
|
||||
KeyboardShortcutManager() {}
|
||||
|
||||
DEPENDENCY_PROPERTY_OWNER(KeyboardShortcutManager);
|
||||
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(Platform::String^, Character);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKey);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyShiftChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyAltChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlShiftChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyInverseChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlInverseChord);
|
||||
|
||||
internal:
|
||||
|
||||
static void Initialize();
|
||||
|
||||
// Sometimes, like with popups, escape is treated as special and even
|
||||
// though it is handled we get it passed through to us. In those cases
|
||||
// we need to be able to ignore it (looking at e->Hanlded isn't sufficient
|
||||
// because that always returns true).
|
||||
// The onlyOnce flag is used to indicate whether we should only ignore the
|
||||
// next escape, or keep ignoring until you explicitly HonorEscape.
|
||||
static void IgnoreEscape(bool onlyOnce);
|
||||
static void HonorEscape();
|
||||
static void HonorShortcuts(bool allow);
|
||||
static void HandledEnter(bool ishandled);
|
||||
static void UpdateDropDownState(bool);
|
||||
static void ShiftButtonChecked(bool checked);
|
||||
static void UpdateDropDownState(Windows::UI::Xaml::Controls::Flyout^ aboutPageFlyout);
|
||||
|
||||
static void RegisterNewAppViewId();
|
||||
static void OnWindowClosed(int viewId);
|
||||
|
||||
private:
|
||||
|
||||
static void OnCharacterPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
Platform::String^ oldValue,
|
||||
Platform::String^ newValue);
|
||||
|
||||
static void OnVirtualKeyPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyControlChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyShiftChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyInverseChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyControlInverseChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyAltChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyControlShiftChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnCharacterReceivedHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args);
|
||||
static void OnKeyDownHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args);
|
||||
static void OnKeyUpHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args);
|
||||
static void OnAcceleratorKeyActivated(Windows::UI::Core::CoreDispatcher^, Windows::UI::Core::AcceleratorKeyEventArgs^ args);
|
||||
};
|
||||
}
|
||||
}
|
||||
554
src/CalcViewModel/Common/LocalizationService.cpp
Normal file
554
src/CalcViewModel/Common/LocalizationService.cpp
Normal file
@@ -0,0 +1,554 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "LocalizationService.h"
|
||||
#include "LocalizationSettings.h"
|
||||
#include "AppResourceProvider.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Common::LocalizationServiceProperties;
|
||||
using namespace Concurrency;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::ApplicationModel::Resources::Core;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace Windows::Globalization::DateTimeFormatting;
|
||||
using namespace Windows::Globalization::Fonts;
|
||||
using namespace Windows::Globalization::NumberFormatting;
|
||||
using namespace Windows::System::UserProfile;
|
||||
using namespace Windows::UI::Text;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
using namespace Windows::UI::Xaml::Documents;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(LocalizationService, FontType);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(LocalizationService, FontSize);
|
||||
|
||||
static reader_writer_lock s_locServiceInstanceLock;
|
||||
|
||||
LocalizationService^ LocalizationService::s_singletonInstance = nullptr;
|
||||
|
||||
// Resources for the engine use numbers as keys. It's inconvenient, but also difficult to
|
||||
// change given that the engine heavily relies on perfect ordering of certain elements.
|
||||
// The key for open parenthesis, '(', is "48".
|
||||
static constexpr auto s_openParenResourceKey = L"48";
|
||||
|
||||
LocalizationService^ LocalizationService::GetInstance()
|
||||
{
|
||||
|
||||
if (s_singletonInstance == nullptr)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_locServiceInstanceLock);
|
||||
|
||||
if (s_singletonInstance == nullptr)
|
||||
{
|
||||
s_singletonInstance = ref new LocalizationService();
|
||||
}
|
||||
}
|
||||
return s_singletonInstance;
|
||||
}
|
||||
|
||||
LocalizationService::LocalizationService()
|
||||
{
|
||||
m_language = ApplicationLanguages::Languages->GetAt(0);
|
||||
m_flowDirection = ResourceContext::GetForCurrentView()->QualifierValues->Lookup(L"LayoutDirection")
|
||||
!= L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight;
|
||||
|
||||
auto resourceLoader = AppResourceProvider::GetInstance();
|
||||
m_fontFamilyOverride = resourceLoader.GetResourceString(L"LocalizedFontFamilyOverride");
|
||||
|
||||
String^ reserved = L"RESERVED_FOR_FONTLOC";
|
||||
|
||||
m_overrideFontApiValues = ((m_fontFamilyOverride != nullptr) && (m_fontFamilyOverride != reserved));
|
||||
if (m_overrideFontApiValues)
|
||||
{
|
||||
String^ localizedUICaptionFontSizeFactorOverride = resourceLoader.GetResourceString(L"LocalizedUICaptionFontSizeFactorOverride");
|
||||
String^ localizedUITextFontSizeFactorOverride = resourceLoader.GetResourceString(L"LocalizedUITextFontSizeFactorOverride");
|
||||
String^ localizedFontWeightOverride = resourceLoader.GetResourceString(L"LocalizedFontWeightOverride");
|
||||
|
||||
// If any of the font overrides are modified then all of them need to be modified
|
||||
assert(localizedFontWeightOverride != reserved);
|
||||
assert(localizedUITextFontSizeFactorOverride != reserved);
|
||||
assert(localizedUICaptionFontSizeFactorOverride != reserved);
|
||||
|
||||
m_fontWeightOverride = ParseFontWeight(localizedFontWeightOverride);
|
||||
m_uiTextFontScaleFactorOverride = _wtof(localizedUITextFontSizeFactorOverride->Data());
|
||||
m_uiCaptionFontScaleFactorOverride = _wtof(localizedUICaptionFontSizeFactorOverride->Data());
|
||||
}
|
||||
|
||||
m_fontGroup = ref new LanguageFontGroup(m_language);
|
||||
}
|
||||
|
||||
FontWeight LocalizationService::ParseFontWeight(String^ fontWeight)
|
||||
{
|
||||
wstring weight = fontWeight->Data();
|
||||
transform(weight.begin(), weight.end(), weight.begin(), towlower);
|
||||
fontWeight = ref new String(weight.c_str());
|
||||
|
||||
if (fontWeight == "black")
|
||||
{
|
||||
return FontWeights::Black;
|
||||
}
|
||||
else if (fontWeight == "bold")
|
||||
{
|
||||
return FontWeights::Bold;
|
||||
}
|
||||
else if (fontWeight == "extrablack")
|
||||
{
|
||||
return FontWeights::ExtraBlack;
|
||||
}
|
||||
else if (fontWeight == "extrabold")
|
||||
{
|
||||
return FontWeights::ExtraBold;
|
||||
}
|
||||
else if (fontWeight == "extralight")
|
||||
{
|
||||
return FontWeights::ExtraLight;
|
||||
}
|
||||
else if (fontWeight == "light")
|
||||
{
|
||||
return FontWeights::Light;
|
||||
}
|
||||
else if (fontWeight == "medium")
|
||||
{
|
||||
return FontWeights::Medium;
|
||||
}
|
||||
else if (fontWeight == "normal")
|
||||
{
|
||||
return FontWeights::Normal;
|
||||
}
|
||||
else if (fontWeight == "semibold")
|
||||
{
|
||||
return FontWeights::SemiBold;
|
||||
}
|
||||
else if (fontWeight == "semilight")
|
||||
{
|
||||
return FontWeights::SemiLight;
|
||||
}
|
||||
else if (fontWeight == "thin")
|
||||
{
|
||||
return FontWeights::Thin;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw invalid_argument("Invalid argument: fontWeight");
|
||||
}
|
||||
}
|
||||
|
||||
FlowDirection LocalizationService::GetFlowDirection()
|
||||
{
|
||||
return m_flowDirection;
|
||||
}
|
||||
|
||||
bool LocalizationService::IsRtlLayout()
|
||||
{
|
||||
return m_flowDirection == FlowDirection::RightToLeft;
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetLanguage()
|
||||
{
|
||||
return m_language;
|
||||
}
|
||||
|
||||
bool LocalizationService::GetOverrideFontApiValues()
|
||||
{
|
||||
return m_overrideFontApiValues;
|
||||
}
|
||||
|
||||
FontFamily^ LocalizationService::GetLanguageFontFamilyForType(LanguageFontType fontType)
|
||||
{
|
||||
if (m_overrideFontApiValues)
|
||||
{
|
||||
return ref new FontFamily(m_fontFamilyOverride);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ref new FontFamily(GetLanguageFont(fontType)->FontFamily);
|
||||
}
|
||||
}
|
||||
|
||||
LanguageFont^ LocalizationService::GetLanguageFont(LanguageFontType fontType)
|
||||
{
|
||||
assert(!m_overrideFontApiValues);
|
||||
assert(m_fontGroup);
|
||||
|
||||
switch (fontType)
|
||||
{
|
||||
case LanguageFontType::UIText:
|
||||
return m_fontGroup->UITextFont;
|
||||
case LanguageFontType::UICaption:
|
||||
return m_fontGroup->UICaptionFont;
|
||||
default:
|
||||
throw std::invalid_argument("fontType");
|
||||
}
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetFontFamilyOverride()
|
||||
{
|
||||
assert(m_overrideFontApiValues);
|
||||
return m_fontFamilyOverride;
|
||||
}
|
||||
|
||||
FontWeight LocalizationService::GetFontWeightOverride()
|
||||
{
|
||||
assert(m_overrideFontApiValues);
|
||||
return m_fontWeightOverride;
|
||||
}
|
||||
|
||||
double LocalizationService::GetFontScaleFactorOverride(LanguageFontType fontType)
|
||||
{
|
||||
assert(m_overrideFontApiValues);
|
||||
|
||||
switch (fontType)
|
||||
{
|
||||
case LanguageFontType::UIText:
|
||||
return m_uiTextFontScaleFactorOverride;
|
||||
case LanguageFontType::UICaption:
|
||||
return m_uiCaptionFontScaleFactorOverride;
|
||||
default:
|
||||
throw invalid_argument("Invalid argument: fontType");
|
||||
}
|
||||
}
|
||||
|
||||
void LocalizationService::OnFontTypePropertyChanged(DependencyObject^ target, LanguageFontType /*oldValue*/, LanguageFontType /*newValue*/)
|
||||
{
|
||||
UpdateFontFamilyAndSize(target);
|
||||
}
|
||||
|
||||
void LocalizationService::OnFontWeightPropertyChanged(DependencyObject^ target, FontWeight /*oldValue*/, FontWeight /*newValue*/)
|
||||
{
|
||||
UpdateFontFamilyAndSize(target);
|
||||
}
|
||||
|
||||
void LocalizationService::OnFontSizePropertyChanged(DependencyObject^ target, double /*oldValue*/, double /*newValue*/)
|
||||
{
|
||||
UpdateFontFamilyAndSize(target);
|
||||
}
|
||||
|
||||
void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target)
|
||||
{
|
||||
FontFamily^ fontFamily;
|
||||
FontWeight fontWeight;
|
||||
bool fOverrideFontWeight = false;
|
||||
double scaleFactor;
|
||||
|
||||
auto service = LocalizationService::GetInstance();
|
||||
auto fontType = LocalizationService::GetFontType(target);
|
||||
|
||||
if (service->GetOverrideFontApiValues())
|
||||
{
|
||||
fontFamily = ref new FontFamily(service->GetFontFamilyOverride());
|
||||
scaleFactor = service->GetFontScaleFactorOverride(fontType) / 100.0;
|
||||
fontWeight = service->GetFontWeightOverride();
|
||||
fOverrideFontWeight = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto languageFont = service->GetLanguageFont(fontType);
|
||||
fontFamily = ref new FontFamily(languageFont->FontFamily);
|
||||
scaleFactor = languageFont->ScaleFactor / 100.0;
|
||||
}
|
||||
|
||||
double sizeToUse = LocalizationService::GetFontSize(target) * scaleFactor;
|
||||
|
||||
auto control = dynamic_cast<Control^>(target);
|
||||
if (control)
|
||||
{
|
||||
control->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
control->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
control->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
control->ClearValue(Control::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto textBlock = dynamic_cast<TextBlock^>(target);
|
||||
if (textBlock)
|
||||
{
|
||||
textBlock->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
textBlock->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
textBlock->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
textBlock->ClearValue(TextBlock::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RichTextBlock^ richTextBlock = dynamic_cast<RichTextBlock^>(target);
|
||||
if (richTextBlock)
|
||||
{
|
||||
richTextBlock->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
richTextBlock->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
richTextBlock->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
richTextBlock->ClearValue(RichTextBlock::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TextElement^ textElement = dynamic_cast<TextElement^>(target);
|
||||
if (textElement)
|
||||
{
|
||||
textElement->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
textElement->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
textElement->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
textElement->ClearValue(TextElement::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If successful, returns a formatter that respects the user's regional format settings,
|
||||
// as configured by running intl.cpl.
|
||||
DecimalFormatter^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter()
|
||||
{
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers != nullptr)
|
||||
{
|
||||
return ref new DecimalFormatter(languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion);
|
||||
}
|
||||
|
||||
return ref new DecimalFormatter();
|
||||
}
|
||||
|
||||
// If successful, returns a formatter that respects the user's regional format settings,
|
||||
// as configured by running intl.cpl.
|
||||
//
|
||||
// This helper function creates a DateTimeFormatter with a TwentyFour hour clock
|
||||
DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String^ format)
|
||||
{
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers == nullptr)
|
||||
{
|
||||
languageIdentifiers = ApplicationLanguages::Languages;
|
||||
}
|
||||
|
||||
return ref new DateTimeFormatter(format, languageIdentifiers);
|
||||
}
|
||||
|
||||
// If successful, returns a formatter that respects the user's regional format settings,
|
||||
// as configured by running intl.cpl.
|
||||
DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(
|
||||
_In_ String^ format,
|
||||
_In_ String^ calendarIdentifier,
|
||||
_In_ String^ clockIdentifier)
|
||||
{
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers == nullptr)
|
||||
{
|
||||
languageIdentifiers = ApplicationLanguages::Languages;
|
||||
}
|
||||
|
||||
return ref new DateTimeFormatter(
|
||||
format,
|
||||
languageIdentifiers,
|
||||
GlobalizationPreferences::HomeGeographicRegion,
|
||||
calendarIdentifier,
|
||||
clockIdentifier);
|
||||
}
|
||||
|
||||
CurrencyFormatter^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatter()
|
||||
{
|
||||
String^ userCurrency = (GlobalizationPreferences::Currencies->Size > 0)
|
||||
? GlobalizationPreferences::Currencies->GetAt(0)
|
||||
: StringReference(DefaultCurrencyCode.data());
|
||||
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers == nullptr)
|
||||
{
|
||||
languageIdentifiers = ApplicationLanguages::Languages;
|
||||
}
|
||||
|
||||
auto currencyFormatter = ref new CurrencyFormatter(
|
||||
userCurrency,
|
||||
languageIdentifiers,
|
||||
GlobalizationPreferences::HomeGeographicRegion);
|
||||
|
||||
int fractionDigits = LocalizationSettings::GetInstance().GetCurrencyTrailingDigits();
|
||||
currencyFormatter->FractionDigits = fractionDigits;
|
||||
|
||||
return currencyFormatter;
|
||||
}
|
||||
|
||||
IIterable<String^>^ LocalizationService::GetLanguageIdentifiers()
|
||||
{
|
||||
WCHAR currentLocale[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
int result = GetUserDefaultLocaleName(currentLocale, LOCALE_NAME_MAX_LENGTH);
|
||||
if (result != 0)
|
||||
{
|
||||
// GetUserDefaultLocaleName may return an invalid bcp47 language tag with trailing non-BCP47 friendly characters,
|
||||
// which if present would start with an underscore, for example sort order
|
||||
// (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx).
|
||||
// Therefore, if there is an underscore in the locale name, trim all characters from the underscore onwards.
|
||||
WCHAR* underscore = wcschr(currentLocale, L'_');
|
||||
if (underscore != nullptr)
|
||||
{
|
||||
*underscore = L'\0';
|
||||
}
|
||||
|
||||
String^ localeString = ref new String(currentLocale);
|
||||
// validate if the locale we have is valid
|
||||
// otherwise we fallback to the default.
|
||||
if (Language::IsWellFormed(localeString))
|
||||
{
|
||||
auto languageList = ref new Vector<String^>();
|
||||
languageList->Append(localeString);
|
||||
return languageList;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unordered_map<wstring, wstring> LocalizationService::GetTokenToReadableNameMap()
|
||||
{
|
||||
// Resources for the engine use numbers as keys. It's inconvenient, but also difficult to
|
||||
// change given that the engine heavily relies on perfect ordering of certain elements.
|
||||
// To compromise, we'll declare a map from engine resource key to automation name from the
|
||||
// standard project resources.
|
||||
static vector<pair<wstring, wstring>> s_parenEngineKeyResourceMap = {
|
||||
// Sine permutations
|
||||
make_pair<wstring, wstring>(L"67", L"SineDegrees"),
|
||||
make_pair<wstring, wstring>(L"73", L"SineRadians"),
|
||||
make_pair<wstring, wstring>(L"79", L"SineGradians"),
|
||||
make_pair<wstring, wstring>(L"70", L"InverseSineDegrees"),
|
||||
make_pair<wstring, wstring>(L"76", L"InverseSineRadians"),
|
||||
make_pair<wstring, wstring>(L"82", L"InverseSineGradians"),
|
||||
make_pair<wstring, wstring>(L"25", L"HyperbolicSine"),
|
||||
make_pair<wstring, wstring>(L"85", L"InverseHyperbolicSine"),
|
||||
|
||||
// Cosine permutations
|
||||
make_pair<wstring, wstring>(L"68", L"CosineDegrees"),
|
||||
make_pair<wstring, wstring>(L"74", L"CosineRadians"),
|
||||
make_pair<wstring, wstring>(L"80", L"CosineGradians"),
|
||||
make_pair<wstring, wstring>(L"71", L"InverseCosineDegrees"),
|
||||
make_pair<wstring, wstring>(L"77", L"InverseCosineRadians"),
|
||||
make_pair<wstring, wstring>(L"83", L"InverseCosineGradians"),
|
||||
make_pair<wstring, wstring>(L"26", L"HyperbolicCosine"),
|
||||
make_pair<wstring, wstring>(L"86", L"InverseHyperbolicCosine"),
|
||||
|
||||
// Tangent permutations
|
||||
make_pair<wstring, wstring>(L"69", L"TangentDegrees"),
|
||||
make_pair<wstring, wstring>(L"75", L"TangentRadians"),
|
||||
make_pair<wstring, wstring>(L"81", L"TangentGradians"),
|
||||
make_pair<wstring, wstring>(L"72", L"InverseTangentDegrees"),
|
||||
make_pair<wstring, wstring>(L"78", L"InverseTangentRadians"),
|
||||
make_pair<wstring, wstring>(L"84", L"InverseTangentGradians"),
|
||||
make_pair<wstring, wstring>(L"27", L"HyperbolicTangent"),
|
||||
make_pair<wstring, wstring>(L"87", L"InverseHyperbolicTangent"),
|
||||
|
||||
// Miscellaneous Scientific functions
|
||||
make_pair<wstring, wstring>(L"94", L"Factorial"),
|
||||
make_pair<wstring, wstring>(L"35", L"DegreeMinuteSecond"),
|
||||
make_pair<wstring, wstring>(L"28", L"NaturalLog"),
|
||||
make_pair<wstring, wstring>(L"91", L"Square")
|
||||
};
|
||||
|
||||
static vector<pair<wstring, wstring>> s_noParenEngineKeyResourceMap = {
|
||||
// Programmer mode functions
|
||||
make_pair<wstring, wstring>(L"9", L"LeftShift"),
|
||||
make_pair<wstring, wstring>(L"10", L"RightShift"),
|
||||
|
||||
// Y Root scientific function
|
||||
make_pair<wstring, wstring>(L"16", L"YRoot")
|
||||
};
|
||||
|
||||
unordered_map<wstring, wstring> tokenToReadableNameMap{};
|
||||
auto resProvider = AppResourceProvider::GetInstance();
|
||||
|
||||
static const wstring openParen = resProvider.GetCEngineString(StringReference(s_openParenResourceKey))->Data();
|
||||
|
||||
for (const auto& keyPair : s_parenEngineKeyResourceMap)
|
||||
{
|
||||
wstring engineStr = resProvider.GetCEngineString(StringReference(keyPair.first.c_str()))->Data();
|
||||
wstring automationName = resProvider.GetResourceString(StringReference(keyPair.second.c_str()))->Data();
|
||||
|
||||
tokenToReadableNameMap.emplace(engineStr + openParen, automationName);
|
||||
}
|
||||
s_parenEngineKeyResourceMap.clear();
|
||||
|
||||
for (const auto& keyPair : s_noParenEngineKeyResourceMap)
|
||||
{
|
||||
wstring engineStr = resProvider.GetCEngineString(StringReference(keyPair.first.c_str()))->Data();
|
||||
wstring automationName = resProvider.GetResourceString(StringReference(keyPair.second.c_str()))->Data();
|
||||
|
||||
tokenToReadableNameMap.emplace(engineStr, automationName);
|
||||
}
|
||||
s_noParenEngineKeyResourceMap.clear();
|
||||
|
||||
// Also replace hyphens with "minus"
|
||||
wstring minusText = resProvider.GetResourceString(L"minus")->Data();
|
||||
tokenToReadableNameMap.emplace(L"-", minusText);
|
||||
|
||||
return tokenToReadableNameMap;
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetNarratorReadableToken(String^ rawToken)
|
||||
{
|
||||
static unordered_map<wstring, wstring> s_tokenToReadableNameMap = GetTokenToReadableNameMap();
|
||||
|
||||
auto itr = s_tokenToReadableNameMap.find(rawToken->Data());
|
||||
if (itr == s_tokenToReadableNameMap.end())
|
||||
{
|
||||
return rawToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const String^ openParen = AppResourceProvider::GetInstance().GetCEngineString(StringReference(s_openParenResourceKey));
|
||||
return ref new String(itr->second.c_str()) + L" " + openParen;
|
||||
}
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetNarratorReadableString(String^ rawString)
|
||||
{
|
||||
wstringstream readableString{};
|
||||
readableString << L"";
|
||||
|
||||
wstring asWstring = rawString->Data();
|
||||
for (const auto& c : asWstring)
|
||||
{
|
||||
readableString << LocalizationService::GetNarratorReadableToken(L"" + c)->Data();
|
||||
}
|
||||
|
||||
return ref new String(readableString.str().c_str());
|
||||
}
|
||||
83
src/CalcViewModel/Common/LocalizationService.h
Normal file
83
src/CalcViewModel/Common/LocalizationService.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp { namespace Common
|
||||
{
|
||||
namespace LocalizationServiceProperties
|
||||
{
|
||||
static constexpr std::wstring_view DefaultCurrencyCode{ L"USD" };
|
||||
}
|
||||
|
||||
public enum class LanguageFontType
|
||||
{
|
||||
UIText,
|
||||
UICaption,
|
||||
};
|
||||
|
||||
public ref class LocalizationService sealed
|
||||
{
|
||||
public:
|
||||
|
||||
DEPENDENCY_PROPERTY_OWNER(LocalizationService);
|
||||
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(LanguageFontType, FontType, LanguageFontType::UIText);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(double, FontSize);
|
||||
|
||||
internal:
|
||||
static LocalizationService^ GetInstance();
|
||||
|
||||
Windows::UI::Xaml::FlowDirection GetFlowDirection();
|
||||
bool IsRtlLayout();
|
||||
bool GetOverrideFontApiValues();
|
||||
Platform::String^ GetLanguage();
|
||||
Windows::UI::Xaml::Media::FontFamily^ GetLanguageFontFamilyForType(LanguageFontType fontType);
|
||||
Platform::String^ GetFontFamilyOverride();
|
||||
Windows::UI::Text::FontWeight GetFontWeightOverride();
|
||||
double GetFontScaleFactorOverride(LanguageFontType fontType);
|
||||
|
||||
static Windows::Globalization::NumberFormatting::DecimalFormatter^ GetRegionalSettingsAwareDecimalFormatter();
|
||||
static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String^ format);
|
||||
static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter(
|
||||
_In_ Platform::String^ format,
|
||||
_In_ Platform::String^ calendarIdentifier,
|
||||
_In_ Platform::String^ clockIdentifier);
|
||||
|
||||
static Windows::Globalization::NumberFormatting::CurrencyFormatter^ GetRegionalSettingsAwareCurrencyFormatter();
|
||||
|
||||
static Platform::String^ GetNarratorReadableToken(Platform::String^ rawToken);
|
||||
static Platform::String^ GetNarratorReadableString(Platform::String^ rawString);
|
||||
|
||||
private:
|
||||
Windows::Globalization::Fonts::LanguageFont^ GetLanguageFont(LanguageFontType fontType);
|
||||
Windows::UI::Text::FontWeight ParseFontWeight(Platform::String^ fontWeight);
|
||||
|
||||
static Windows::Foundation::Collections::IIterable<Platform::String^>^ GetLanguageIdentifiers();
|
||||
|
||||
// Attached property callbacks
|
||||
static void OnFontTypePropertyChanged(Windows::UI::Xaml::DependencyObject^ target, LanguageFontType oldValue, LanguageFontType newValue);
|
||||
static void OnFontWeightPropertyChanged(Windows::UI::Xaml::DependencyObject^ target, Windows::UI::Text::FontWeight oldValue, Windows::UI::Text::FontWeight newValue);
|
||||
static void OnFontSizePropertyChanged(Windows::UI::Xaml::DependencyObject^ target, double oldValue, double newValue);
|
||||
|
||||
static void UpdateFontFamilyAndSize(Windows::UI::Xaml::DependencyObject^ target);
|
||||
|
||||
static std::unordered_map<std::wstring, std::wstring> GetTokenToReadableNameMap();
|
||||
|
||||
private:
|
||||
LocalizationService();
|
||||
|
||||
static LocalizationService^ s_singletonInstance;
|
||||
|
||||
Windows::Globalization::Fonts::LanguageFontGroup^ m_fontGroup;
|
||||
Platform::String^ m_language;
|
||||
Windows::UI::Xaml::FlowDirection m_flowDirection;
|
||||
bool m_overrideFontApiValues;
|
||||
Platform::String^ m_fontFamilyOverride;
|
||||
Windows::UI::Text::FontWeight m_fontWeightOverride;
|
||||
double m_uiTextFontScaleFactorOverride;
|
||||
double m_uiCaptionFontScaleFactorOverride;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
385
src/CalcViewModel/Common/LocalizationSettings.h
Normal file
385
src/CalcViewModel/Common/LocalizationSettings.h
Normal file
@@ -0,0 +1,385 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "LocalizationService.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class LocalizationSettings
|
||||
{
|
||||
private:
|
||||
LocalizationSettings()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
// Use DecimalFormatter as it respects the locale and the user setting
|
||||
Windows::Globalization::NumberFormatting::DecimalFormatter^ formatter;
|
||||
formatter = CalculatorApp::Common::LocalizationService::GetRegionalSettingsAwareDecimalFormatter();
|
||||
formatter->FractionDigits = 0;
|
||||
formatter->IsDecimalPointAlwaysDisplayed = false;
|
||||
|
||||
for (unsigned int i = 0; i < 10; i++)
|
||||
{
|
||||
m_digitSymbols.at(i) = formatter->FormatUInt(i)->Data()[0];
|
||||
}
|
||||
|
||||
result = ResolveLocaleName(formatter->ResolvedLanguage->Data(),
|
||||
m_resolvedName,
|
||||
LOCALE_NAME_MAX_LENGTH);
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error resolving locale name");
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t decimalString[LocaleSettingBufferSize] = L"";
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_SDECIMAL,
|
||||
decimalString,
|
||||
ARRAYSIZE(decimalString));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
wchar_t groupingSymbolString[LocaleSettingBufferSize] = L"";
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_STHOUSAND,
|
||||
groupingSymbolString,
|
||||
ARRAYSIZE(groupingSymbolString));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
wchar_t numberGroupingString[LocaleSettingBufferSize] = L"";
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_SGROUPING,
|
||||
numberGroupingString,
|
||||
ARRAYSIZE(numberGroupingString));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
// Get locale info for List Separator, eg. comma is used in many locales
|
||||
wchar_t listSeparatorString[4] = L"";
|
||||
result = ::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_SLIST,
|
||||
listSeparatorString,
|
||||
ARRAYSIZE(listSeparatorString)); // Max length of the expected return value is 4
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
int currencyTrailingDigits = 0;
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER,
|
||||
(LPWSTR)¤cyTrailingDigits,
|
||||
sizeof(currencyTrailingDigits) / sizeof(WCHAR));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
// Currency symbol precedence is either 0 or 1.
|
||||
// A value of 0 indicates the symbol follows the currency value.
|
||||
int currencySymbolPrecedence = 1;
|
||||
result = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_IPOSSYMPRECEDES | LOCALE_RETURN_NUMBER,
|
||||
(LPWSTR)¤cySymbolPrecedence,
|
||||
sizeof(currencySymbolPrecedence) / sizeof(WCHAR));
|
||||
|
||||
// As CalcEngine only supports the first character of the decimal separator,
|
||||
// Only first character of the decimal separator string is supported.
|
||||
m_decimalSeparator = decimalString[0];
|
||||
m_numberGroupSeparator = groupingSymbolString[0];
|
||||
m_numberGrouping = numberGroupingString;
|
||||
m_listSeparator = listSeparatorString;
|
||||
m_currencyTrailingDigits = currencyTrailingDigits;
|
||||
m_currencySymbolPrecedence = currencySymbolPrecedence;
|
||||
}
|
||||
|
||||
// Get the system calendar type
|
||||
// Note: This function returns 0 on failure.
|
||||
// We'll ignore the failure in that case and the CalendarIdentifier would get set to GregorianCalendar.
|
||||
CALID calId;
|
||||
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
|
||||
reinterpret_cast<PWSTR>(&calId),
|
||||
sizeof(calId));
|
||||
|
||||
m_calendarIdentifier = GetCalendarIdentifierFromCalid(calId);
|
||||
|
||||
// Get FirstDayOfWeek Date and Time setting
|
||||
wchar_t day[80] = L"";
|
||||
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_IFIRSTDAYOFWEEK, // The first day in a week
|
||||
reinterpret_cast<PWSTR>(day), // Argument is of type PWSTR
|
||||
ARRAYSIZE(day)); // Max return size are 80 characters
|
||||
|
||||
// The LOCALE_IFIRSTDAYOFWEEK integer value varies from 0, 1, .. 6 for Monday, Tuesday, ... Sunday
|
||||
// DayOfWeek enum value varies from 0, 1, .. 6 for Sunday, Monday, ... Saturday
|
||||
// Hence, DayOfWeek = (valueof(LOCALE_IFIRSTDAYOFWEEK) + 1) % 7
|
||||
m_firstDayOfWeek = static_cast<Windows::Globalization::DayOfWeek>((_wtoi(day) + 1) % 7); // static cast int to DayOfWeek enum
|
||||
}
|
||||
|
||||
public:
|
||||
// A LocalizationSettings object is not copyable.
|
||||
LocalizationSettings(const LocalizationSettings&) = delete;
|
||||
LocalizationSettings& operator=(const LocalizationSettings&) = delete;
|
||||
|
||||
// A LocalizationSettings object is not moveable.
|
||||
LocalizationSettings(LocalizationSettings&&) = delete;
|
||||
LocalizationSettings& operator=(LocalizationSettings&&) = delete;
|
||||
|
||||
// Provider of the singleton LocalizationSettings instance.
|
||||
static const LocalizationSettings& GetInstance()
|
||||
{
|
||||
static const LocalizationSettings localizationSettings;
|
||||
|
||||
return localizationSettings;
|
||||
}
|
||||
|
||||
Platform::String^ GetLocaleName() const
|
||||
{
|
||||
return ref new Platform::String(m_resolvedName);
|
||||
}
|
||||
|
||||
bool IsDigitEnUsSetting() const
|
||||
{
|
||||
if (this->GetDigitSymbolFromEnUsDigit('0') == L'0')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocalizeDisplayValue(_Inout_ std::wstring* stringToLocalize) const
|
||||
{
|
||||
if (IsDigitEnUsSetting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (wchar_t& ch : *stringToLocalize)
|
||||
{
|
||||
if (IsEnUsDigit(ch))
|
||||
{
|
||||
ch = GetDigitSymbolFromEnUsDigit(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ GetEnglishValueFromLocalizedDigits(const std::wstring& localizedString) const
|
||||
{
|
||||
if (m_resolvedName == L"en-US")
|
||||
{
|
||||
return ref new Platform::String(localizedString.c_str());
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
size_t length = localizedString.size();
|
||||
std::unique_ptr<wchar_t[]> englishString(new wchar_t[length + 1]); // +1 for the null termination
|
||||
|
||||
for (; i < length; ++i)
|
||||
{
|
||||
wchar_t ch = localizedString[i];
|
||||
if (!IsEnUsDigit(ch))
|
||||
{
|
||||
for (int j = 0; j < 10; ++j)
|
||||
{
|
||||
if (ch == m_digitSymbols[j])
|
||||
{
|
||||
ch = j.ToString()->Data()[0];
|
||||
break;
|
||||
//ch = val - L'0';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch == m_decimalSeparator)
|
||||
{
|
||||
ch = L'.';
|
||||
}
|
||||
englishString[i] = ch;
|
||||
}
|
||||
englishString[i] = '\0';
|
||||
|
||||
return ref new Platform::String(englishString.get());
|
||||
}
|
||||
|
||||
bool IsEnUsDigit(const wchar_t digit) const
|
||||
{
|
||||
if (digit >= L'0' && digit <= L'9')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLocalizedDigit(const wchar_t digit) const
|
||||
{
|
||||
for (auto dig : m_digitSymbols)
|
||||
{
|
||||
if (digit == dig)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLocalizedHexDigit(const wchar_t digit) const
|
||||
{
|
||||
if (IsLocalizedDigit(digit))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto dig : s_hexSymbols)
|
||||
{
|
||||
if (digit == dig)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t GetDigitSymbolFromEnUsDigit(wchar_t digitSymbol) const
|
||||
{
|
||||
assert(digitSymbol >= L'0' && digitSymbol <= L'9');
|
||||
int digit = digitSymbol - L'0';
|
||||
return m_digitSymbols.at(digit); // throws on out of range
|
||||
}
|
||||
|
||||
wchar_t GetDecimalSeparator() const
|
||||
{
|
||||
return m_decimalSeparator;
|
||||
}
|
||||
|
||||
wchar_t GetNumberGroupSeparator() const
|
||||
{
|
||||
return m_numberGroupSeparator;
|
||||
}
|
||||
|
||||
std::wstring GetDecimalSeparatorStr() const
|
||||
{
|
||||
std::wstring result;
|
||||
result.push_back(m_decimalSeparator);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring GetNumberGroupingSeparatorStr() const
|
||||
{
|
||||
std::wstring result;
|
||||
result.push_back(m_numberGroupSeparator);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring GetNumberGroupingStr() const
|
||||
{
|
||||
return m_numberGrouping;
|
||||
}
|
||||
|
||||
void RemoveGroupSeparators(const wchar_t* value, const size_t length, std::wstring* rawValue) const
|
||||
{
|
||||
rawValue->clear();
|
||||
rawValue->reserve(length);
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
if (value[i] != L' ' && value[i] != m_numberGroupSeparator)
|
||||
{
|
||||
rawValue->append(1, value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ GetCalendarIdentifier() const
|
||||
{
|
||||
return m_calendarIdentifier;
|
||||
}
|
||||
|
||||
std::wstring GetListSeparator() const
|
||||
{
|
||||
return m_listSeparator;
|
||||
}
|
||||
|
||||
Windows::Globalization::DayOfWeek GetFirstDayOfWeek() const
|
||||
{
|
||||
return m_firstDayOfWeek;
|
||||
}
|
||||
|
||||
int GetCurrencyTrailingDigits() const
|
||||
{
|
||||
return m_currencyTrailingDigits;
|
||||
}
|
||||
|
||||
int GetCurrencySymbolPrecedence() const
|
||||
{
|
||||
return m_currencySymbolPrecedence;
|
||||
}
|
||||
|
||||
private:
|
||||
static Platform::String^ GetCalendarIdentifierFromCalid(CALID calId)
|
||||
{
|
||||
switch (calId)
|
||||
{
|
||||
case CAL_GREGORIAN:
|
||||
case CAL_GREGORIAN_ARABIC:
|
||||
case CAL_GREGORIAN_ME_FRENCH:
|
||||
case CAL_GREGORIAN_US:
|
||||
case CAL_GREGORIAN_XLIT_ENGLISH:
|
||||
case CAL_GREGORIAN_XLIT_FRENCH:
|
||||
return Windows::Globalization::CalendarIdentifiers::Gregorian;
|
||||
|
||||
case CAL_HEBREW:
|
||||
return Windows::Globalization::CalendarIdentifiers::Hebrew;
|
||||
|
||||
case CAL_HIJRI:
|
||||
case CAL_PERSIAN:
|
||||
return Windows::Globalization::CalendarIdentifiers::Hijri;
|
||||
|
||||
case CAL_JAPAN:
|
||||
return Windows::Globalization::CalendarIdentifiers::Japanese;
|
||||
|
||||
case CAL_KOREA:
|
||||
return Windows::Globalization::CalendarIdentifiers::Korean;
|
||||
|
||||
case CAL_TAIWAN:
|
||||
return Windows::Globalization::CalendarIdentifiers::Taiwan;
|
||||
|
||||
case CAL_THAI:
|
||||
return Windows::Globalization::CalendarIdentifiers::Thai;
|
||||
|
||||
case CAL_UMALQURA:
|
||||
return Windows::Globalization::CalendarIdentifiers::UmAlQura;
|
||||
|
||||
// Gregorian will be the default Calendar Type
|
||||
default:
|
||||
return Windows::Globalization::CalendarIdentifiers::Gregorian;
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t m_decimalSeparator;
|
||||
wchar_t m_numberGroupSeparator;
|
||||
std::wstring m_numberGrouping;
|
||||
std::array<wchar_t, 10> m_digitSymbols;
|
||||
// Hexadecimal characters are not currently localized
|
||||
static constexpr std::array<wchar_t, 6> s_hexSymbols{ L'A', L'B', L'C', L'D', L'E', L'F' };
|
||||
std::wstring m_listSeparator;
|
||||
Platform::String^ m_calendarIdentifier;
|
||||
Windows::Globalization::DayOfWeek m_firstDayOfWeek;
|
||||
int m_currencySymbolPrecedence;
|
||||
wchar_t m_resolvedName[LOCALE_NAME_MAX_LENGTH];
|
||||
int m_currencyTrailingDigits;
|
||||
static const unsigned int LocaleSettingBufferSize = 16;
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user