calculator/src/CalcManager/CEngine/calc.cpp

187 lines
6.1 KiB
C++

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#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.
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_precedenceOpCount(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);
m_maxTrigonometricNum = RationalMath::Pow(10, 100, m_radix, m_precision);
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++)
{
auto maxVal = m_chopNumbers[i].Div(2, m_precision);
maxVal = RationalMath::Integer(maxVal, m_radix, m_precision);
m_maxDecimalValueStrings[i] = maxVal.ToString(10, FMT_FLOAT, m_precision);
}
}
// 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;
}