Merged PR 10790341: [Recall] Update calculator engine with snapshot for further calculations

####What
When restoring from snapshot, we need to set calculator engine properly to make further calculations correct.

####How
Update calculator engine by a serial of corresponding commands from snapshot.

To get the commands for the display area when saving snapshot,
1. If the expression is not from history and the primary display is the result of the expression, `DisplayCommands` of `StandardCalculatorSnapshot` will be empty, and we will use the commands from `ExpressionDisplay` for restoring in the future.
2. If the expression is not from history and the primary display is not the result of the expression, `DisplayCommands` of `StandardCalculatorSnapshot` will be the commands from the history collector in addition to the operand command in the primary display, and it will be used for restoring in the future.
3. If the expression and primary display are from history, `DisplayCommands` will be incomplete with the operand command in the primary display missing as by current design of history, and the commands from `ExpressionDisplay` will be used for restoring in the future.

Related work items: #51002745
This commit is contained in:
Han Zhang 👾 2024-05-21 07:22:32 +00:00
commit 089f7deb08
9 changed files with 161 additions and 96 deletions

View file

@ -3,7 +3,6 @@
#include "Header Files/CalcEngine.h"
#include "Command.h"
#include "ExpressionCommand.h"
#include "winerror_cross_platform.h"
constexpr int ASCII_0 = 48;
@ -66,47 +65,7 @@ CHistoryCollector::~CHistoryCollector()
void CHistoryCollector::AddOpndToHistory(wstring_view numStr, Rational const& rat, bool fRepetition)
{
std::shared_ptr<std::vector<int>> commands = std::make_shared<vector<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)
{
commands->push_back(IDC_PNT);
if (!fSciFmt)
{
fDecimal = true;
}
}
else if (numStr[i] == L'e')
{
commands->push_back(IDC_EXP);
fSciFmt = true;
}
else if (numStr[i] == L'-')
{
commands->push_back(IDC_SIGN);
}
else if (numStr[i] == L'+')
{
// Ignore.
}
// Number
else
{
int num = static_cast<int>(numStr[i]) - ASCII_0;
num += IDC_0;
commands->push_back(num);
}
}
auto operandCommand = std::make_shared<COpndCommand>(commands, fNegative, fDecimal, fSciFmt);
operandCommand->Initialize(rat);
int iCommandEnd = AddCommand(operandCommand);
int iCommandEnd = AddCommand(GetOperandCommandsFromString(numStr, rat));
m_lastOpStartIndex = IchAddSzToEquationSz(numStr, iCommandEnd);
if (fRepetition)
@ -201,7 +160,7 @@ void CHistoryCollector::EnclosePrecInversionBrackets()
IchAddSzToEquationSz(CCalcEngine::OpCodeToString(IDC_CLOSEP), -1);
}
bool CHistoryCollector::FOpndAddedToHistory()
bool CHistoryCollector::FOpndAddedToHistory() const
{
return (-1 != m_lastOpStartIndex);
}
@ -465,7 +424,7 @@ void CHistoryCollector::SetDecimalSymbol(wchar_t decimalSymbol)
}
// Update the commands corresponding to the passed string Number
std::shared_ptr<std::vector<int>> CHistoryCollector::GetOperandCommandsFromString(wstring_view numStr)
std::shared_ptr<std::vector<int>> CHistoryCollector::GetOperandCommandsFromString(wstring_view numStr) const
{
std::shared_ptr<std::vector<int>> commands = std::make_shared<std::vector<int>>();
// Check for negate
@ -505,3 +464,58 @@ std::shared_ptr<std::vector<int>> CHistoryCollector::GetOperandCommandsFromStrin
}
return commands;
}
std::shared_ptr<COpndCommand> CHistoryCollector::GetOperandCommandsFromString(std::wstring_view numStr, Rational const& rat) const
{
std::shared_ptr<std::vector<int>> commands = std::make_shared<vector<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)
{
commands->push_back(IDC_PNT);
if (!fSciFmt)
{
fDecimal = true;
}
}
else if (numStr[i] == L'e')
{
commands->push_back(IDC_EXP);
fSciFmt = true;
}
else if (numStr[i] == L'-')
{
commands->push_back(IDC_SIGN);
}
else if (numStr[i] == L'+')
{
// Ignore.
}
// Number
else
{
int num = static_cast<int>(numStr[i]) - ASCII_0;
num += IDC_0;
commands->push_back(num);
}
}
auto operandCommand = std::make_shared<COpndCommand>(commands, fNegative, fDecimal, fSciFmt);
operandCommand->Initialize(rat);
return operandCommand;
}
std::vector<std::shared_ptr<IExpressionCommand>> CHistoryCollector::GetCommands() const
{
std::vector<std::shared_ptr<IExpressionCommand>> commands;
if (m_spCommands != nullptr)
{
commands = *m_spCommands;
}
return commands;
}

View file

@ -202,3 +202,13 @@ wchar_t CCalcEngine::DecimalSeparator() const
{
return m_decimalSeparator;
}
std::vector<std::shared_ptr<IExpressionCommand>> CCalcEngine::GetHistoryCollectorCommandsSnapshot() const
{
auto commands = m_HistoryCollector.GetCommands();
if (!m_HistoryCollector.FOpndAddedToHistory() && m_bRecord)
{
commands.push_back(m_HistoryCollector.GetOperandCommandsFromString(m_numberString, m_currentVal));
}
return commands;
}

View file

@ -597,4 +597,9 @@ namespace CalculationManager
{
m_inHistoryItemLoadMode = isHistoryItemLoadMode;
}
std::vector<std::shared_ptr<IExpressionCommand>> CalculatorManager::GetDisplayCommandsSnapshot() const
{
return m_currentCalculatorEngine->GetHistoryCollectorCommandsSnapshot();
}
}

View file

@ -119,5 +119,6 @@ namespace CalculationManager
}
CalculationManager::Command GetCurrentDegreeMode();
void SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode);
std::vector<std::shared_ptr<IExpressionCommand>> GetDisplayCommandsSnapshot() const;
};
}

View file

@ -89,6 +89,8 @@ public:
void UpdateMaxIntDigits();
wchar_t DecimalSeparator() const;
std::vector<std::shared_ptr<IExpressionCommand>> GetHistoryCollectorCommandsSnapshot() const;
// Static methods for the instance
static void
InitialOneTimeOnlySetup(CalculationManager::IResourceProvider& resourceProvider); // Once per load time to call to initialize all shared global variables

View file

@ -4,10 +4,13 @@
#pragma once
#include <array>
#include "ExpressionCommand.h"
#include "ICalcDisplay.h"
#include "IHistoryDisplay.h"
#include "Rational.h"
class COpndCommand;
// maximum depth you can get by precedence. It is just an array's size limit.
static constexpr size_t MAXPRECDEPTH = 25;
@ -29,13 +32,15 @@ public:
void PushLastOpndStart(int ichOpndStart = -1);
void PopLastOpndStart();
void EnclosePrecInversionBrackets();
bool FOpndAddedToHistory();
bool FOpndAddedToHistory() const;
void CompleteHistoryLine(std::wstring_view numStr);
void CompleteEquation(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);
std::shared_ptr<COpndCommand> GetOperandCommandsFromString(std::wstring_view numStr, CalcEngine::Rational const& rat) const;
std::vector<std::shared_ptr<IExpressionCommand>> GetCommands() const;
private:
std::shared_ptr<IHistoryDisplay> m_pHistoryDisplay;
@ -60,5 +65,5 @@ private:
void TruncateEquationSzFromIch(int ich);
void SetExpressionDisplay();
void InsertSzInEquationSz(std::wstring_view str, int icommandIndex, int ich);
std::shared_ptr<std::vector<int>> GetOperandCommandsFromString(std::wstring_view numStr);
std::shared_ptr<std::vector<int>> GetOperandCommandsFromString(std::wstring_view numStr) const;
};

View file

@ -63,6 +63,12 @@ namespace
{
jsonObject->SetNamedValue(L"ExpressionDisplay", SaveSnapshotToJson(*value.ExpressionDisplay));
}
auto commandsJsonArray = ref new Windows::Data::Json::JsonArray();
for (const auto& command : value.DisplayCommands)
{
commandsJsonArray->Append(SaveSnapshotToJson(command));
}
jsonObject->SetNamedValue(L"DisplayCommands", commandsJsonArray);
return jsonObject;
}
@ -270,6 +276,7 @@ namespace
return;
}
}
standardCalculatorSnapshot.DisplayCommands = RestoreExpressionCommandsFromJsonArray(jsonObject->GetNamedArray(L"DisplayCommands"));
value = std::move(standardCalculatorSnapshot);
}

View file

@ -42,6 +42,57 @@ namespace
StringReference IsBitFlipCheckedPropertyName(L"IsBitFlipChecked");
StringReference CalcAlwaysOnTop(L"CalcAlwaysOnTop");
StringReference CalcBackToFullView(L"CalcBackToFullView");
std::vector<int> GetCommandsFromExpressionCommands(const std::vector<std::shared_ptr<IExpressionCommand>>& expressionCommands)
{
vector<int> commands;
for (const auto& command : expressionCommands)
{
CommandType commandType = command->GetCommandType();
if (commandType == CommandType::UnaryCommand)
{
shared_ptr<IUnaryCommand> spCommand = dynamic_pointer_cast<IUnaryCommand>(command);
const shared_ptr<vector<int>>& unaryCommands = spCommand->GetCommands();
for (int nUCode : *unaryCommands)
{
commands.push_back(nUCode);
}
}
if (commandType == CommandType::BinaryCommand)
{
shared_ptr<IBinaryCommand> spCommand = dynamic_pointer_cast<IBinaryCommand>(command);
commands.push_back(spCommand->GetCommand());
}
if (commandType == CommandType::Parentheses)
{
shared_ptr<IParenthesisCommand> spCommand = dynamic_pointer_cast<IParenthesisCommand>(command);
commands.push_back(spCommand->GetCommand());
}
if (commandType == CommandType::OperandCommand)
{
shared_ptr<IOpndCommand> spCommand = dynamic_pointer_cast<IOpndCommand>(command);
const shared_ptr<vector<int>>& opndCommands = spCommand->GetCommands();
bool fNeedIDCSign = spCommand->IsNegative();
for (int nOCode : *opndCommands)
{
commands.push_back(nOCode);
if (fNeedIDCSign && nOCode != IDC_0)
{
commands.push_back(static_cast<int>(CalculationManager::Command::CommandSIGN));
fNeedIDCSign = false;
}
}
}
}
return commands;
}
}
namespace CalculatorResourceKeys
@ -1388,55 +1439,8 @@ void StandardCalculatorViewModel::Recalculate(bool fromHistory)
{
// Recalculate
Command currentDegreeMode = m_standardCalculatorManager.GetCurrentDegreeMode();
shared_ptr<vector<shared_ptr<IExpressionCommand>>> savedCommands = make_shared<vector<shared_ptr<IExpressionCommand>>>();
vector<int> currentCommands;
for (const auto& command : *m_commands)
{
savedCommands->push_back(command);
CommandType commandType = command->GetCommandType();
if (commandType == CommandType::UnaryCommand)
{
shared_ptr<IUnaryCommand> spCommand = dynamic_pointer_cast<IUnaryCommand>(command);
const shared_ptr<vector<int>>& unaryCommands = spCommand->GetCommands();
for (int nUCode : *unaryCommands)
{
currentCommands.push_back(nUCode);
}
}
if (commandType == CommandType::BinaryCommand)
{
shared_ptr<IBinaryCommand> spCommand = dynamic_pointer_cast<IBinaryCommand>(command);
currentCommands.push_back(spCommand->GetCommand());
}
if (commandType == CommandType::Parentheses)
{
shared_ptr<IParenthesisCommand> spCommand = dynamic_pointer_cast<IParenthesisCommand>(command);
currentCommands.push_back(spCommand->GetCommand());
}
if (commandType == CommandType::OperandCommand)
{
shared_ptr<IOpndCommand> spCommand = dynamic_pointer_cast<IOpndCommand>(command);
const shared_ptr<vector<int>>& opndCommands = spCommand->GetCommands();
bool fNeedIDCSign = spCommand->IsNegative();
for (int nOCode : *opndCommands)
{
currentCommands.push_back(nOCode);
if (fNeedIDCSign && nOCode != IDC_0)
{
currentCommands.push_back(static_cast<int>(CalculationManager::Command::CommandSIGN));
fNeedIDCSign = false;
}
}
}
}
shared_ptr<vector<shared_ptr<IExpressionCommand>>> savedCommands = std::make_shared<std::vector<shared_ptr<IExpressionCommand>>>(*m_commands);
vector<int> currentCommands = GetCommandsFromExpressionCommands(*m_commands);
shared_ptr<vector<pair<wstring, int>>> savedTokens = make_shared<vector<pair<wstring, int>>>();
@ -1796,6 +1800,7 @@ StandardCalculatorSnapshot StandardCalculatorViewModel::GetStandardCalculatorSna
{
snapshot.ExpressionDisplay = { *m_tokens, *m_commands };
}
snapshot.DisplayCommands = m_standardCalculatorManager.GetDisplayCommandsSnapshot();
return snapshot;
}
@ -1805,11 +1810,26 @@ void StandardCalculatorViewModel::SetStandardCalculatorSnapshot(const StandardCa
{
m_standardCalculatorManager.SetHistoryItems(snapshot.CalcManager.HistoryItems.value());
}
SetPrimaryDisplay(snapshot.PrimaryDisplay.DisplayValue, snapshot.PrimaryDisplay.IsError);
std::vector<int> commands;
if (snapshot.ExpressionDisplay.has_value() && snapshot.ExpressionDisplay->Tokens.back().first == L"=")
{
commands = GetCommandsFromExpressionCommands(snapshot.ExpressionDisplay->Commands);
}
if (commands.empty() && !snapshot.DisplayCommands.empty())
{
commands = GetCommandsFromExpressionCommands(snapshot.DisplayCommands);
}
for (const auto& command : commands)
{
m_standardCalculatorManager.SendCommand(static_cast<Command>(command));
}
if (snapshot.ExpressionDisplay.has_value())
{
SetExpressionDisplay(
std::make_shared<std::vector<std::pair<std::wstring, int>>>(snapshot.ExpressionDisplay->Tokens),
std::make_shared<std::vector<std::shared_ptr<IExpressionCommand>>>(snapshot.ExpressionDisplay->Commands));
}
SetPrimaryDisplay(snapshot.PrimaryDisplay.DisplayValue, snapshot.PrimaryDisplay.IsError);
}

View file

@ -55,6 +55,7 @@ namespace CalculatorApp
CalculatorManagerSnapshot CalcManager;
PrimaryDisplaySnapshot PrimaryDisplay;
std::optional<ExpressionDisplaySnapshot> ExpressionDisplay;
std::vector<std::shared_ptr<IExpressionCommand>> DisplayCommands;
};
[Windows::UI::Xaml::Data::Bindable] public ref class StandardCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged