Merge master into feature/GraphingCalculator branch (#660)

This commit is contained in:
Pepe Rivera
2019-09-05 15:01:12 -07:00
committed by GitHub
parent 1c9755d38a
commit 41fbcfe9c5
199 changed files with 5809 additions and 2925 deletions

View File

@@ -951,7 +951,7 @@ int CCalcEngine::GetCurrentRadix()
return m_radix;
}
wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision)
wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix)
{
Rational rat = (m_bRecord ? m_input.ToRational(m_radix, m_precision) : m_currentVal);
@@ -964,7 +964,14 @@ wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision)
ChangeConstants(m_radix, m_precision);
}
return GroupDigitsPerRadix(numberString, radix);
if (groupDigitsPerRadix)
{
return GroupDigitsPerRadix(numberString, radix);
}
else
{
return numberString;
}
}
wstring CCalcEngine::GetStringForDisplay(Rational const& rat, uint32_t radix)

View File

@@ -45,8 +45,8 @@
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">10.0.18362.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 -->
@@ -309,6 +309,7 @@
<ClInclude Include="Ratpack\CalcErr.h" />
<ClInclude Include="Ratpack\ratconst.h" />
<ClInclude Include="Ratpack\ratpak.h" />
<ClInclude Include="NumberFormattingUtils.h" />
<ClInclude Include="UnitConverter.h" />
</ItemGroup>
<ItemGroup>
@@ -349,6 +350,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="NumberFormattingUtils.cpp" />
<ClCompile Include="UnitConverter.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@@ -89,6 +89,7 @@
<ClCompile Include="CEngine\RationalMath.cpp">
<Filter>CEngine</Filter>
</ClCompile>
<ClCompile Include="NumberFormattingUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Command.h" />
@@ -160,5 +161,6 @@
<ClInclude Include="Header Files\RationalMath.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NumberFormattingUtils.h" />
</ItemGroup>
</Project>
</Project>

View File

@@ -37,15 +37,6 @@ namespace CalculationManager
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
@@ -622,9 +613,9 @@ namespace CalculationManager
}
}
wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision)
wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix)
{
return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision) : L"";
return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision, groupDigitsPerRadix) : L"";
}
void CalculatorManager::SetPrecision(int32_t precision)

View File

@@ -103,7 +103,6 @@ namespace CalculationManager
void MemoryItemChanged(unsigned int indexOfMemory) override;
CalculatorManager(ICalcDisplay* displayCallback, IResourceProvider* resourceProvider);
~CalculatorManager();
void Reset(bool clearMemory = true);
void SetStandardMode();
@@ -125,7 +124,7 @@ namespace CalculationManager
}
void SetRadix(RADIX_TYPE iRadixType);
void SetMemorizedNumbersString();
std::wstring GetResultForRadix(uint32_t radix, int32_t precision);
std::wstring GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix);
void SetPrecision(int32_t precision);
void UpdateMaxIntDigits();
wchar_t DecimalSeparator();

View File

@@ -137,6 +137,7 @@ void COpndCommand::AppendCommand(int command)
{
m_commands->Append(command);
}
if (command == IDC_PNT)
{
m_fDecimal = true;
@@ -256,38 +257,30 @@ const wstring& COpndCommand::GetToken(wchar_t decimalSymbol)
}
// 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;
m_token.erase(0, i - 1);
}
else
{
trimIdx = i;
m_token.erase(0, i);
}
fDigitsFound = true;
break;
if (m_fNegative)
{
m_token.insert(0, &chNegate);
}
return m_token;
}
}
if (fDigitsFound)
{
m_token.erase(0, trimIdx);
if (m_fNegative)
{
m_token.insert(0, &chNegate);
}
}
else
{
m_token.clear();
m_token.append(&chZero);
}
m_token.clear();
m_token.append(&chZero);
return m_token;
}

View File

@@ -75,7 +75,7 @@ public:
void SettingsChanged();
bool IsCurrentTooBigForTrig();
int GetCurrentRadix();
std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision);
std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix);
void ChangePrecision(int32_t precision)
{
m_precision = precision;

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "NumberFormattingUtils.h"
using namespace std;
namespace CalcManager::NumberFormattingUtils
{
/// <summary>
/// Trims out any trailing zeros or decimals in the given input string
/// </summary>
/// <param name="number">number to trim</param>
void TrimTrailingZeros(_Inout_ wstring& number)
{
if (number.find(L'.') == wstring::npos)
{
return;
}
wstring::iterator iter;
for (iter = number.end() - 1;; iter--)
{
if (*iter != L'0')
{
number.erase(iter + 1, number.end());
break;
}
}
if (*(number.end() - 1) == L'.')
{
number.erase(number.end() - 1, number.end());
}
}
/// <summary>
/// Get number of digits (whole number part + decimal part)</summary>
/// <param name="value">the number</param>
unsigned int GetNumberDigits(wstring value)
{
TrimTrailingZeros(value);
unsigned int numberSignificantDigits = static_cast<unsigned int>(value.size());
if (value.find(L'.') != wstring::npos)
{
--numberSignificantDigits;
}
if (value.find(L'-') != wstring::npos)
{
--numberSignificantDigits;
}
return numberSignificantDigits;
}
/// <summary>
/// Get number of digits (whole number part only)</summary>
/// <param name="value">the number</param>
unsigned int GetNumberDigitsWholeNumberPart(double value)
{
return value == 0 ? 1 : (1 + (int)log10(abs(value)));
}
/// <summary>
/// Rounds the given double to the given number of significant digits
/// </summary>
/// <param name="num">input double</param>
/// <param name="numSignificant">int number of significant digits to round to</param>
wstring RoundSignificantDigits(double num, int numSignificant)
{
wstringstream out(wstringstream::out);
out << fixed;
out.precision(numSignificant);
out << num;
return out.str();
}
/// <summary>
/// Convert a Number to Scientific Notation
/// </summary>
/// <param name="number">number to convert</param>
wstring ToScientificNumber(double number)
{
wstringstream out(wstringstream::out);
out << scientific << number;
return out.str();
}
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include <string>
namespace CalcManager::NumberFormattingUtils
{
void TrimTrailingZeros(_Inout_ std::wstring& input);
unsigned int GetNumberDigits(std::wstring value);
unsigned int GetNumberDigitsWholeNumberPart(double value);
std::wstring RoundSignificantDigits(double value, int numberSignificantDigits);
std::wstring ToScientificNumber(double number);
}

View File

@@ -7,9 +7,11 @@
#include <algorithm> // for std::sort
#include "Command.h"
#include "UnitConverter.h"
#include "NumberFormattingUtils.h"
using namespace std;
using namespace UnitConversionManager;
using namespace CalcManager::NumberFormattingUtils;
static constexpr uint32_t EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT = 3;
static constexpr uint32_t EXPECTEDSERIALIZEDUNITTOKENCOUNT = 6;
@@ -178,7 +180,7 @@ void UnitConverter::SwitchActive(const wstring& newValue)
swap(m_currentHasDecimal, m_returnHasDecimal);
m_returnDisplay = m_currentDisplay;
m_currentDisplay = newValue;
m_currentHasDecimal = (m_currentDisplay.find(L'.') != m_currentDisplay.npos);
m_currentHasDecimal = (m_currentDisplay.find(L'.') != wstring::npos);
m_switchedActive = true;
if (m_currencyDataLoader != nullptr && m_vmCurrencyCallback != nullptr)
@@ -202,7 +204,7 @@ vector<wstring> UnitConverter::StringToVector(const wstring& w, const wchar_t* d
size_t delimiterIndex = w.find(delimiter);
size_t startIndex = 0;
vector<wstring> serializedTokens = vector<wstring>();
while (delimiterIndex != w.npos)
while (delimiterIndex != wstring::npos)
{
serializedTokens.push_back(w.substr(startIndex, delimiterIndex - startIndex));
startIndex = delimiterIndex + (int)wcslen(delimiter);
@@ -634,19 +636,19 @@ vector<tuple<wstring, Unit>> UnitConverter::CalculateSuggested()
wstring roundedString;
if (abs(entry.value) < 100)
{
roundedString = RoundSignificant(entry.value, 2);
roundedString = RoundSignificantDigits(entry.value, 2);
}
else if (abs(entry.value) < 1000)
{
roundedString = RoundSignificant(entry.value, 1);
roundedString = RoundSignificantDigits(entry.value, 1);
}
else
{
roundedString = RoundSignificant(entry.value, 0);
roundedString = RoundSignificantDigits(entry.value, 0);
}
if (stod(roundedString) != 0.0 || m_currentCategory.supportsNegative)
{
TrimString(roundedString);
TrimTrailingZeros(roundedString);
returnVector.push_back(make_tuple(roundedString, entry.type));
}
}
@@ -672,21 +674,21 @@ vector<tuple<wstring, Unit>> UnitConverter::CalculateSuggested()
wstring roundedString;
if (abs(entry.value) < 100)
{
roundedString = RoundSignificant(entry.value, 2);
roundedString = RoundSignificantDigits(entry.value, 2);
}
else if (abs(entry.value) < 1000)
{
roundedString = RoundSignificant(entry.value, 1);
roundedString = RoundSignificantDigits(entry.value, 1);
}
else
{
roundedString = RoundSignificant(entry.value, 0);
roundedString = RoundSignificantDigits(entry.value, 0);
}
// How to work out which is the best whimsical value to add to the vector?
if (stod(roundedString) != 0.0)
{
TrimString(roundedString);
TrimTrailingZeros(roundedString);
whimsicalReturnVector.push_back(make_tuple(roundedString, entry.type));
}
}
@@ -789,17 +791,27 @@ void UnitConverter::InitializeSelectedUnits()
vector<Unit> curUnits = itr->second;
if (!curUnits.empty())
{
// Units may already have been initialized through UnitConverter::RestoreUserPreferences().
// Check if they have been, and if so, do not override restored units.
bool isFromUnitValid = m_fromType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_fromType) != curUnits.end();
bool isToUnitValid = m_toType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_toType) != curUnits.end();
if (isFromUnitValid && isToUnitValid)
{
return;
}
bool conversionSourceSet = false;
bool conversionTargetSet = false;
for (const Unit& cur : curUnits)
{
if (!conversionSourceSet && cur.isConversionSource)
if (!conversionSourceSet && cur.isConversionSource && !isFromUnitValid)
{
m_fromType = cur;
conversionSourceSet = true;
}
if (!conversionTargetSet && cur.isConversionTarget)
if (!conversionTargetSet && cur.isConversionTarget && !isToUnitValid)
{
m_toType = cur;
conversionTargetSet = true;
@@ -843,100 +855,61 @@ void UnitConverter::Calculate()
{
m_returnDisplay = m_currentDisplay;
m_returnHasDecimal = m_currentHasDecimal;
TrimString(m_returnDisplay);
TrimTrailingZeros(m_returnDisplay);
UpdateViewModel();
return;
}
unordered_map<Unit, ConversionData, UnitHash> conversionTable = m_ratioMap[m_fromType];
double returnValue = stod(m_currentDisplay);
if (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0)
if (AnyUnitIsEmpty() || (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0))
{
m_returnDisplay = m_currentDisplay;
m_returnHasDecimal = m_currentHasDecimal;
TrimString(m_returnDisplay);
TrimTrailingZeros(m_returnDisplay);
}
else
{
returnValue = Convert(returnValue, conversionTable[m_toType]);
m_returnDisplay = RoundSignificant(returnValue, MAXIMUMDIGITSALLOWED);
TrimString(m_returnDisplay);
int numPreDecimal = (int)m_returnDisplay.size();
if (m_returnDisplay.find(L'.') != m_returnDisplay.npos)
{
numPreDecimal = (int)m_returnDisplay.find(L'.');
}
if (returnValue < 0)
{
numPreDecimal--;
}
double currentValue = stod(m_currentDisplay);
double returnValue = Convert(currentValue, conversionTable[m_toType]);
if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED))
auto isCurrencyConverter = m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(this->m_currentCategory);
if (isCurrencyConverter)
{
wstringstream out(wstringstream::out);
out << scientific << returnValue;
m_returnDisplay = out.str();
// We don't need to trim the value when it's a currency.
m_returnDisplay = RoundSignificantDigits(returnValue, MAXIMUMDIGITSALLOWED);
TrimTrailingZeros(m_returnDisplay);
}
else
{
returnValue = stod(m_returnDisplay);
wstring returnString;
if (m_currentDisplay.size() <= OPTIMALDIGITSALLOWED && abs(returnValue) >= OPTIMALDECIMALALLOWED)
int numPreDecimal = GetNumberDigitsWholeNumberPart(returnValue);
if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED))
{
returnString = RoundSignificant(returnValue, OPTIMALDIGITSALLOWED - min(numPreDecimal, OPTIMALDIGITSALLOWED));
m_returnDisplay = ToScientificNumber(returnValue);
}
else
{
returnString = RoundSignificant(returnValue, MAXIMUMDIGITSALLOWED - min(numPreDecimal, MAXIMUMDIGITSALLOWED));
int currentNumberSignificantDigits = GetNumberDigits(m_currentDisplay);
int precision = 0;
if (abs(returnValue) < OPTIMALDECIMALALLOWED)
{
precision = MAXIMUMDIGITSALLOWED;
}
else
{
// Fewer digits are needed following the decimal if the number is large,
// we calculate the number of decimals necessary based on the number of digits in the integer part.
precision = max(0, max(OPTIMALDIGITSALLOWED, min(MAXIMUMDIGITSALLOWED, currentNumberSignificantDigits)) - numPreDecimal);
}
m_returnDisplay = RoundSignificantDigits(returnValue, precision);
TrimTrailingZeros(m_returnDisplay);
}
m_returnDisplay = returnString;
TrimString(m_returnDisplay);
m_returnHasDecimal = (m_returnDisplay.find(L'.') != wstring::npos);
}
m_returnHasDecimal = (m_returnDisplay.find(L'.') != m_returnDisplay.npos);
}
UpdateViewModel();
}
/// <summary>
/// Trims out any trailing zeros or decimals in the given input string
/// </summary>
/// <param name="input">wstring to trim</param>
void UnitConverter::TrimString(wstring& returnString)
{
if (returnString.find(L'.') == m_returnDisplay.npos)
{
return;
}
wstring::iterator iter;
for (iter = returnString.end() - 1;; iter--)
{
if (*iter != L'0')
{
returnString.erase(iter + 1, returnString.end());
break;
}
}
if (*(returnString.end() - 1) == L'.')
{
returnString.erase(returnString.end() - 1, returnString.end());
}
}
/// <summary>
/// Rounds the given double to the given number of significant digits
/// </summary>
/// <param name="num">input double</param>
/// <param name="numSignificant">int number of significant digits to round to</param>
wstring UnitConverter::RoundSignificant(double num, int numSignificant)
{
wstringstream out(wstringstream::out);
out << fixed;
out.precision(numSignificant);
out << num;
return out.str();
}
void UnitConverter::UpdateCurrencySymbols()
{
if (m_currencyDataLoader != nullptr && m_vmCurrencyCallback != nullptr)

View File

@@ -279,9 +279,7 @@ namespace UnitConversionManager
double Convert(double value, ConversionData conversionData);
std::vector<std::tuple<std::wstring, Unit>> CalculateSuggested();
void ClearValues();
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);

View File

@@ -20,3 +20,5 @@
#include <unordered_map>
#include <vector>
#include <winerror.h>
#include <iostream>
#include <math.h>