Description of the changes: Currently Calculator handles strings by defining integers for each type of function that can be performed, this integer will eventually correspond with an index in s_engineStrings which holds the corresponding display string for each function. Some functions such as Sin can have multiple strings (degrees, rads, grads, inverse). Functions like Sin are mapped to another array called "rgUfne" where a new integer is given depending on the output string which will then be given to s_engineStrings. The new integer returned by the "rgUfne" array runs the risk of overlapping with any new functions that may be added in CCommand.h. Furthermore, it is expected that the strings in s_engineStrings and rgUfne are defined in a particular order (not necessarily sequential), otherwise the logic will break. This makes adding new strings for new functions confusing and difficult, since a lot of the logic is not clearly defined. This PR attempts to make this a bit simpler by changing the s_engineStrings and rgUfne arrays to be unordered_maps instead of arrays. For s_engineStrings the keys will now be strings, allowing the existing logic for indexing to be used by simply converting the number into a string to access the value. This will also allow us to create keys in the future that are not limited to integers but to strings that hold more meaning. The rgUfne array will also be updated to be a map that will take in an integer and give you the corresponding string that can be passed to s_engineStrings. The UFNE object in the rgUfne array will also be updated to hold all the possible string keys for a function, instead of indexing them on other numbers that may overlap with existing definitions. Now to add a new string for a new IDC_FOO function, we would just need to add the "FooString" resource keys to the g_sids array and use the updated rgUfne map to link the IDC_FOO value to the corresponding "FooString" resource key. This way the resource key can be a meaningful string, and not an integer that must be in any particular order. How changes were validated: Tested each function manually in standard, scientific, and programmer modes.
192 lines
6.2 KiB
C++
192 lines
6.2 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 int32_t 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.
|
|
|
|
unordered_map<wstring, wstring> CCalcEngine::s_engineStrings;
|
|
|
|
void CCalcEngine::LoadEngineStrings(CalculationManager::IResourceProvider& resourceProvider)
|
|
{
|
|
for (const auto& sid : g_sids)
|
|
{
|
|
auto locKey = wstring{ sid };
|
|
auto locString = resourceProvider.GetCEngineString(locKey);
|
|
if (!locString.empty())
|
|
{
|
|
s_engineStrings[locKey] = locString;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
//
|
|
// InitialOneTimeOnlyNumberSetup
|
|
//
|
|
//////////////////////////////////////////////////
|
|
void CCalcEngine::InitialOneTimeOnlySetup(CalculationManager::IResourceProvider& resourceProvider)
|
|
{
|
|
LoadEngineStrings(resourceProvider);
|
|
|
|
// we must now set up 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_fPrecedence(fPrecedence),
|
|
m_fIntegerMode(fIntegerMode),
|
|
m_pCalcDisplay(pCalcDisplay),
|
|
m_resourceProvider(pResourceProvider),
|
|
m_nOpCode(0),
|
|
m_nPrevOpCode(0),
|
|
m_bChangeOp(false),
|
|
m_bRecord(false),
|
|
m_bSetCalcState(false),
|
|
m_input(DEFAULT_DEC_SEPARATOR),
|
|
m_nFE(FMT_FLOAT),
|
|
m_memoryValue{ make_unique<Rational>() },
|
|
m_holdVal{},
|
|
m_currentVal{},
|
|
m_lastVal{},
|
|
m_parenVals{},
|
|
m_precedenceVals{},
|
|
m_bError(false),
|
|
m_bInv(false),
|
|
m_bNoPrevEqu(true),
|
|
m_radix(DEFAULT_RADIX),
|
|
m_precision(DEFAULT_PRECISION),
|
|
m_cIntDigitsSav(DEFAULT_MAX_DIGITS),
|
|
m_decGrouping(),
|
|
m_numberString(DEFAULT_NUMBER_STR),
|
|
m_nTempCom(0),
|
|
m_openParenCount(0),
|
|
m_nOp(),
|
|
m_nPrecOp(),
|
|
m_precedenceOpCount(0),
|
|
m_nLastCom(0),
|
|
m_angletype(ANGLE_DEG),
|
|
m_numwidth(QWORD_WIDTH),
|
|
m_HistoryCollector(pCalcDisplay, pHistoryDisplay, DEFAULT_DEC_SEPARATOR),
|
|
m_groupSeparator(DEFAULT_GRP_SEPARATOR)
|
|
{
|
|
InitChopNumbers();
|
|
|
|
m_dwWordBitWidth = DwWordBitWidthFromeNumWidth(m_numwidth);
|
|
|
|
m_maxTrigonometricNum = RationalMath::Pow(10, 100);
|
|
|
|
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 lengths
|
|
// 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] / 2;
|
|
maxVal = RationalMath::Integer(maxVal);
|
|
|
|
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[SIDS_DECIMAL_SEPARATOR] = 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;
|
|
}
|