diff --git a/.gitattributes b/.gitattributes index 51617f5f..e0d31f91 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,29 +12,6 @@ ############################################################################### *.cs diff=csharp -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -*.sln merge=binary -*.csproj merge=binary -*.vbproj merge=binary -*.vcxproj merge=binary -*.vcproj merge=binary -*.dbproj merge=binary -*.fsproj merge=binary -*.lsproj merge=binary -*.wixproj merge=binary -*.modelproj merge=binary -*.sqlproj merge=binary -*.wwaproj merge=binary - ############################################################################### # behavior for image files # diff --git a/.gitignore b/.gitignore index 2100dfc1..1088272d 100644 --- a/.gitignore +++ b/.gitignore @@ -289,6 +289,7 @@ __pycache__/ # Calculator specific Generated Files/ +src/GraphControl/GraphingImplOverrides.props !/build/config/TRexDefs/** !src/Calculator/TemporaryKey.pfx !src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx \ No newline at end of file diff --git a/README.md b/README.md index d967b175..00243142 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Calculator ships regularly with new features and bug fixes. You can get the late - Calculation history and memory capabilities. - Conversion between many units of measurement. - Currency conversion based on data retrieved from [Bing](https://www.bing.com). +- [Infinite precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) for basic + arithmetic operations (addition, subtraction, multiplication, division) so that calculations + never lose precision. ## Getting started Prerequisites: diff --git a/build/config/SignConfig.xml b/build/config/SignConfig.xml index 7209725e..eb298e87 100644 --- a/build/config/SignConfig.xml +++ b/build/config/SignConfig.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/build/pipelines/azure-pipelines.ci.yaml b/build/pipelines/azure-pipelines.ci.yaml index 31ce98fb..23eb46ca 100644 --- a/build/pipelines/azure-pipelines.ci.yaml +++ b/build/pipelines/azure-pipelines.ci.yaml @@ -33,6 +33,10 @@ jobs: platform: ARM64 condition: not(eq(variables['Build.Reason'], 'PullRequest')) +- template: ./templates/run-ui-tests.yaml + parameters: + platform: x64 + - template: ./templates/run-unit-tests.yaml parameters: platform: x64 diff --git a/build/pipelines/templates/build-single-architecture.yaml b/build/pipelines/templates/build-single-architecture.yaml index 92e06f7b..50859db3 100644 --- a/build/pipelines/templates/build-single-architecture.yaml +++ b/build/pipelines/templates/build-single-architecture.yaml @@ -30,7 +30,7 @@ steps: inputs: solution: src/Calculator.sln vsVersion: 15.0 - msbuildArgs: /bl:$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\Calculator.binlog /p:OutDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\ /p:GenerateProjectSpecificOutputFolder=true /p:AppVersion=$(Build.BuildNumber) ${{ parameters.extraMsBuildArgs }} + msbuildArgs: /bl:$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\Calculator.binlog /p:OutDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\ /p:GenerateProjectSpecificOutputFolder=true /p:AppVersion=$(Build.BuildNumber) /t:Publish /p:PublishDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\publish\ ${{ parameters.extraMsBuildArgs }} platform: $(BuildPlatform) configuration: $(BuildConfiguration) clean: true diff --git a/build/pipelines/templates/prepare-release-internalonly.yaml b/build/pipelines/templates/prepare-release-internalonly.yaml index 363ca049..178fd268 100644 --- a/build/pipelines/templates/prepare-release-internalonly.yaml +++ b/build/pipelines/templates/prepare-release-internalonly.yaml @@ -1,5 +1,5 @@ # This template contains a job which builds artifacts needed to release the app to the store and to -# Windows using Microsoft-internal systems. It relies Microsoft-internal resources and will not +# Windows using Microsoft-internal systems. It relies on Microsoft-internal resources and will not # work outside of Microsoft. # Specifically, this job: # - Signs the bundle using a secure system. If you want to build your own, use SignTool following @@ -28,8 +28,8 @@ jobs: - task: PkgESSetupBuild@10 displayName: Initialize Package ES inputs: - productName: Calculator - disableWorkspace: true + productName: Calculator + disableWorkspace: true env: XES_DISABLEPROV: true @@ -46,6 +46,8 @@ jobs: - task: PkgESCodeSign@10 displayName: Send bundle to Package ES code signing service + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: signConfigXml: build\config\SignConfig.xml inPathRoot: $(Build.ArtifactStagingDirectory)\appxBundle @@ -60,25 +62,25 @@ jobs: - task: CopyFiles@2 displayName: Copy signed AppxBundle to vpack staging folder inputs: - sourceFolder: $(Build.ArtifactStagingDirectory)\appxBundleSigned - targetFolder: $(Build.ArtifactStagingDirectory)\vpack\appxBundle + sourceFolder: $(Build.ArtifactStagingDirectory)\appxBundleSigned + targetFolder: $(Build.ArtifactStagingDirectory)\vpack\appxBundle - task: PkgESVPack@10 displayName: Create and push vpack for app env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: - sourceDirectory: $(Build.ArtifactStagingDirectory)\vpack\appxBundle - description: VPack for the Calculator Application - pushPkgName: calculator.app - version: $(versionMajor).$(versionMinor).$(versionBuild) - owner: paxeeapps + sourceDirectory: $(Build.ArtifactStagingDirectory)\vpack\appxBundle + description: VPack for the Calculator Application + pushPkgName: calculator.app + version: $(versionMajor).$(versionMinor).$(versionBuild) + owner: paxeeapps - task: PublishBuildArtifacts@1 displayName: Publish vpack\app artifact with vpack manifest inputs: - pathtoPublish: $(XES_VPACKMANIFESTDIRECTORY)\$(XES_VPACKMANIFESTNAME) - artifactName: vpack\app + pathtoPublish: $(XES_VPACKMANIFESTDIRECTORY)\$(XES_VPACKMANIFESTNAME) + artifactName: vpack\app # TODO (macool): create and push internal test packages and test config diff --git a/build/pipelines/templates/run-ui-tests.yaml b/build/pipelines/templates/run-ui-tests.yaml new file mode 100644 index 00000000..41d441ad --- /dev/null +++ b/build/pipelines/templates/run-ui-tests.yaml @@ -0,0 +1,52 @@ +# This template contains jobs to run UI tests using WinAppDriver. + +parameters: + platform: '' + +jobs: +- job: UITests${{ parameters.platform }} + displayName: UITests ${{ parameters.platform }} + dependsOn: Build${{ parameters.platform }} + condition: succeeded() + pool: + vmImage: windows-2019 + variables: + skipComponentGovernanceDetection: true + steps: + - checkout: none + + - powershell: Set-DisplayResolution -Width 1920 -Height 1080 -Force + displayName: Set resolution to 1920x1080 + continueOnError: true + + - task: DownloadBuildArtifacts@0 + displayName: Download AppxBundle and CalculatorUITests + inputs: + artifactName: drop + itemPattern: | + drop/Release/${{ parameters.platform }}/Calculator/AppPackages/** + drop/Release/${{ parameters.platform }}/publish/** + + - task: PowerShell@2 + displayName: Install certificate + inputs: + filePath: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Add-AppDevPackage.ps1 + arguments: -CertificatePath $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Calculator_$(Build.BuildNumber)_${{ parameters.platform }}.cer -Force + + - task: PowerShell@2 + displayName: Install app + inputs: + filePath: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Add-AppDevPackage.ps1 + arguments: -Force + + - powershell: Start-Process -FilePath "C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe" -Verb RunAs + displayName: Start WinAppDriver + + - task: VSTest@2 + displayName: Run CalculatorUITests + inputs: + testAssemblyVer2: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/CalculatorUITests.dll + vsTestVersion: 16.0 + runSettingsFile: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/CalculatorUITests.runsettings + platform: ${{ parameters.platform }} + configuration: Release \ No newline at end of file diff --git a/docs/ApplicationArchitecture.md b/docs/ApplicationArchitecture.md index 0d7c2ff3..dff02965 100644 --- a/docs/ApplicationArchitecture.md +++ b/docs/ApplicationArchitecture.md @@ -153,7 +153,9 @@ The CalcEngine contains the logic for interpreting and performing operations acc ### RatPack -The RatPack (short for Rational Pack) is the core of the Calculator model and contains the logic for performing its mathematical operations. The interface to this layer is defined in [ratpak.h][ratpak.h]. +The RatPack (short for Rational Pack) is the core of the Calculator model and contains the logic for +performing its mathematical operations (using [infinite precision][Infinite Precision] arithmetic +instead of regular floating point arithmetic). The interface to this layer is defined in [ratpak.h][ratpak.h]. [References]:#################################################################################################### diff --git a/src/CalcManager/CEngine/CalcInput.cpp b/src/CalcManager/CEngine/CalcInput.cpp index 45e553b9..1c47a8da 100644 --- a/src/CalcManager/CEngine/CalcInput.cpp +++ b/src/CalcManager/CEngine/CalcInput.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "Header Files/CalcEngine.h" using namespace std; diff --git a/src/CalcManager/CEngine/CalcUtils.cpp b/src/CalcManager/CEngine/CalcUtils.cpp index 7980e4e0..15b11926 100644 --- a/src/CalcManager/CEngine/CalcUtils.cpp +++ b/src/CalcManager/CEngine/CalcUtils.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" #include "Header Files/CalcUtils.h" diff --git a/src/CalcManager/CEngine/History.cpp b/src/CalcManager/CEngine/History.cpp index ca276a41..74edf3cc 100644 --- a/src/CalcManager/CEngine/History.cpp +++ b/src/CalcManager/CEngine/History.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" #include "Command.h" #include "CalculatorVector.h" diff --git a/src/CalcManager/CEngine/Number.cpp b/src/CalcManager/CEngine/Number.cpp index 4bf83a33..fdf29e09 100644 --- a/src/CalcManager/CEngine/Number.cpp +++ b/src/CalcManager/CEngine/Number.cpp @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -#include "pch.h" +#include #include "Header Files/Number.h" using namespace std; diff --git a/src/CalcManager/CEngine/Rational.cpp b/src/CalcManager/CEngine/Rational.cpp index fd86b72a..e116c237 100644 --- a/src/CalcManager/CEngine/Rational.cpp +++ b/src/CalcManager/CEngine/Rational.cpp @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -#include "pch.h" +#include #include "Header Files/Rational.h" using namespace std; @@ -182,6 +182,13 @@ namespace CalcEngine return *this; } + /// + /// Calculate the remainder after division, the sign of a result will match the sign of the current object. + /// + /// + /// This function has the same behavior as the standard C/C++ operator '%' + /// to calculate the modulus after division instead, use instead. + /// Rational& Rational::operator%=(Rational const& rhs) { PRAT lhsRat = this->ToPRAT(); @@ -189,7 +196,7 @@ namespace CalcEngine try { - modrat(&lhsRat, rhsRat); + remrat(&lhsRat, rhsRat); destroyrat(rhsRat); } catch (uint32_t error) @@ -342,6 +349,12 @@ namespace CalcEngine return lhs; } + /// + /// Calculate the remainder after division, the sign of a result will match the sign of lhs. + /// + /// + /// This function has the same behavior as the standard C/C++ operator '%', to calculate the modulus after division instead, use instead. + /// Rational operator%(Rational lhs, Rational const& rhs) { lhs %= rhs; diff --git a/src/CalcManager/CEngine/RationalMath.cpp b/src/CalcManager/CEngine/RationalMath.cpp index 37dc7926..4b1a4b8a 100644 --- a/src/CalcManager/CEngine/RationalMath.cpp +++ b/src/CalcManager/CEngine/RationalMath.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/RationalMath.h" using namespace std; @@ -387,3 +386,33 @@ Rational RationalMath::ATanh(Rational const& rat) return result; } + +/// +/// Calculate the modulus after division, the sign of the result will match the sign of b. +/// +/// +/// When one of the operand is negative +/// the result will differ from the C/C++ operator '%' +/// use instead to calculate the remainder after division. +/// +Rational RationalMath::Mod(Rational const& a, Rational const& b) +{ + PRAT prat = a.ToPRAT(); + PRAT pn = b.ToPRAT(); + + try + { + modrat(&prat, pn); + destroyrat(pn); + } + catch (uint32_t error) + { + destroyrat(prat); + destroyrat(pn); + throw(error); + } + + auto res = Rational{ prat }; + destroyrat(prat); + return res; +} diff --git a/src/CalcManager/CEngine/calc.cpp b/src/CalcManager/CEngine/calc.cpp index 0e4979f2..65e6f0d0 100644 --- a/src/CalcManager/CEngine/calc.cpp +++ b/src/CalcManager/CEngine/calc.cpp @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "Header Files/CalcEngine.h" - #include "CalculatorResource.h" using namespace std; diff --git a/src/CalcManager/CEngine/scicomm.cpp b/src/CalcManager/CEngine/scicomm.cpp index d450a70d..36130bb5 100644 --- a/src/CalcManager/CEngine/scicomm.cpp +++ b/src/CalcManager/CEngine/scicomm.cpp @@ -12,7 +12,8 @@ * * Author: \****************************************************************************/ -#include "pch.h" + +#include #include "Header Files/CalcEngine.h" #include "Header Files/CalcUtils.h" @@ -390,7 +391,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) cleared for CENTR */ if (nullptr != m_pCalcDisplay) { - m_pCalcDisplay->SetParenDisplayText(L""); + m_pCalcDisplay->SetParenthesisNumber(0); m_pCalcDisplay->SetExpressionDisplay(make_shared>>(), make_shared>>()); } @@ -594,7 +595,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) // Set the "(=xx" indicator. if (nullptr != m_pCalcDisplay) { - m_pCalcDisplay->SetParenDisplayText(m_openParenCount ? to_wstring(m_openParenCount) : L""); + m_pCalcDisplay->SetParenthesisNumber(m_openParenCount >= 0 ? static_cast(m_openParenCount) : 0); } if (!m_bError) diff --git a/src/CalcManager/CEngine/scidisp.cpp b/src/CalcManager/CEngine/scidisp.cpp index 849fe863..1ef88799 100644 --- a/src/CalcManager/CEngine/scidisp.cpp +++ b/src/CalcManager/CEngine/scidisp.cpp @@ -12,7 +12,9 @@ * * Author: \****************************************************************************/ -#include "pch.h" + +#include +#include #include "Header Files/CalcEngine.h" using namespace std; diff --git a/src/CalcManager/CEngine/scifunc.cpp b/src/CalcManager/CEngine/scifunc.cpp index a1007c1c..c2341752 100644 --- a/src/CalcManager/CEngine/scifunc.cpp +++ b/src/CalcManager/CEngine/scifunc.cpp @@ -16,7 +16,6 @@ /*** ***/ /*** ***/ /**************************************************************************/ -#include "pch.h" #include "Header Files/CalcEngine.h" using namespace std; diff --git a/src/CalcManager/CEngine/scioper.cpp b/src/CalcManager/CEngine/scioper.cpp index b09083c4..f8d04855 100644 --- a/src/CalcManager/CEngine/scioper.cpp +++ b/src/CalcManager/CEngine/scioper.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" using namespace CalcEngine; @@ -78,7 +77,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa case IDC_DIV: case IDC_MOD: { - int iNumeratorSign = 1, iDenominatorSign = 1, iFinalSign = 1; + int iNumeratorSign = 1, iDenominatorSign = 1; auto temp = result; result = rhs; @@ -107,20 +106,30 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa if (operation == IDC_DIV) { - iFinalSign = iNumeratorSign * iDenominatorSign; result /= temp; + if (m_fIntegerMode && (iNumeratorSign * iDenominatorSign) == -1) + { + result = -(Integer(result)); + } } else { - iFinalSign = iNumeratorSign; - result %= temp; - } + if (m_fIntegerMode) + { + // Programmer mode, use remrat (remainder after division) + result %= temp; - if (m_fIntegerMode && iFinalSign == -1) - { - result = -(Integer(result)); + if (iNumeratorSign == -1) + { + result = -(Integer(result)); + } + } + else + { + //other modes, use modrat (modulus after division) + result = Mod(result, temp); + } } - break; } diff --git a/src/CalcManager/CEngine/sciset.cpp b/src/CalcManager/CEngine/sciset.cpp index 5495748e..ca715be5 100644 --- a/src/CalcManager/CEngine/sciset.cpp +++ b/src/CalcManager/CEngine/sciset.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" using namespace CalcEngine; diff --git a/src/CalcManager/CalcManager.vcxproj b/src/CalcManager/CalcManager.vcxproj index 4a098141..4d03a5c9 100644 --- a/src/CalcManager/CalcManager.vcxproj +++ b/src/CalcManager/CalcManager.vcxproj @@ -157,6 +157,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -173,6 +174,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -189,6 +191,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -205,6 +208,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -222,6 +226,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -238,6 +243,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -254,6 +260,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -270,6 +277,7 @@ $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -278,7 +286,6 @@ - diff --git a/src/CalcManager/CalculatorHistory.cpp b/src/CalcManager/CalculatorHistory.cpp index 31a1dae2..935ea8da 100644 --- a/src/CalcManager/CalculatorHistory.cpp +++ b/src/CalcManager/CalculatorHistory.cpp @@ -1,7 +1,7 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "CalculatorHistory.h" using namespace std; diff --git a/src/CalcManager/CalculatorHistory.h b/src/CalcManager/CalculatorHistory.h index 8116e9e4..ea87f857 100644 --- a/src/CalcManager/CalculatorHistory.h +++ b/src/CalcManager/CalculatorHistory.h @@ -38,7 +38,7 @@ namespace CalculationManager void ClearHistory(); unsigned int AddItem(_In_ std::shared_ptr const &spHistoryItem); bool RemoveItem(unsigned int uIdx); - const size_t MaxHistorySize() const { return m_maxHistorySize; } + size_t MaxHistorySize() const { return m_maxHistorySize; } ~CalculatorHistory(void); private: diff --git a/src/CalcManager/CalculatorManager.cpp b/src/CalcManager/CalculatorManager.cpp index b4b4dfaf..e640bd94 100644 --- a/src/CalcManager/CalculatorManager.cpp +++ b/src/CalcManager/CalculatorManager.cpp @@ -1,7 +1,7 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include // for UCHAR_MAX #include "Header Files/CalcEngine.h" #include "CalculatorManager.h" #include "CalculatorResource.h" @@ -111,9 +111,9 @@ namespace CalculationManager /// Callback from the engine /// /// string containing the parenthesis count - void CalculatorManager::SetParenDisplayText(const wstring& parenthesisCount) + void CalculatorManager::SetParenthesisNumber(_In_ unsigned int parenthesisCount) { - m_displayCallback->SetParenDisplayText(parenthesisCount); + m_displayCallback->SetParenthesisNumber(parenthesisCount); } /// diff --git a/src/CalcManager/CalculatorManager.h b/src/CalcManager/CalculatorManager.h index 0b9986b0..e00dc243 100644 --- a/src/CalcManager/CalculatorManager.h +++ b/src/CalcManager/CalculatorManager.h @@ -94,7 +94,7 @@ namespace CalculationManager void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands) override; void SetMemorizedNumbers(_In_ const std::vector& memorizedNumbers) override; void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) override; - void SetParenDisplayText(const std::wstring& parenthesisCount) override; + void SetParenthesisNumber(_In_ unsigned int parenthesisCount) override; void OnNoRightParenAdded() override; void DisplayPasteError(); void MaxDigitsReached() override; diff --git a/src/CalcManager/CalculatorVector.h b/src/CalcManager/CalculatorVector.h index a4de434d..e9f1b8dc 100644 --- a/src/CalcManager/CalculatorVector.h +++ b/src/CalcManager/CalculatorVector.h @@ -1,9 +1,14 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once +#include +#include +#include #include "Ratpack/CalcErr.h" +#include // for std::out_of_range +#include // for SAL template class CalculatorVector diff --git a/src/CalcManager/Command.h b/src/CalcManager/Command.h index e6eeb8ac..4d98d012 100644 --- a/src/CalcManager/Command.h +++ b/src/CalcManager/Command.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once diff --git a/src/CalcManager/ExpressionCommand.cpp b/src/CalcManager/ExpressionCommand.cpp index 0b68a374..9fc87708 100644 --- a/src/CalcManager/ExpressionCommand.cpp +++ b/src/CalcManager/ExpressionCommand.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "Header Files/CCommand.h" #include "CalculatorVector.h" #include "ExpressionCommand.h" diff --git a/src/CalcManager/ExpressionCommandInterface.h b/src/CalcManager/ExpressionCommandInterface.h index ce89dd81..94bae836 100644 --- a/src/CalcManager/ExpressionCommandInterface.h +++ b/src/CalcManager/ExpressionCommandInterface.h @@ -1,7 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once + +#include // for std::shared_ptr #include "CalculatorVector.h" #include "Command.h" diff --git a/src/CalcManager/Header Files/CCommand.h b/src/CalcManager/Header Files/CCommand.h index 2fc08b3c..9530b5c5 100644 --- a/src/CalcManager/Header Files/CCommand.h +++ b/src/CalcManager/Header Files/CCommand.h @@ -13,6 +13,8 @@ * \****************************************************************************/ +#pragma once + // The following are the valid id's which can be passed to CCalcEngine::ProcessCommand #define IDM_HEX 313 diff --git a/src/CalcManager/Header Files/CalcEngine.h b/src/CalcManager/Header Files/CalcEngine.h index db906e51..c84f2d55 100644 --- a/src/CalcManager/Header Files/CalcEngine.h +++ b/src/CalcManager/Header Files/CalcEngine.h @@ -45,7 +45,7 @@ namespace CalculationManager class IResourceProvider; } -namespace CalculatorUnitTests +namespace CalculatorEngineTests { class CalcEngineTests; } @@ -160,5 +160,5 @@ private: static void ChangeBaseConstants(uint32_t radix, int maxIntDigits, int32_t precision); void BaseOrPrecisionChanged(); - friend class CalculatorUnitTests::CalcEngineTests; + friend class CalculatorEngineTests::CalcEngineTests; }; diff --git a/src/CalcManager/Header Files/EngineStrings.h b/src/CalcManager/Header Files/EngineStrings.h index 37f11483..89d624bf 100644 --- a/src/CalcManager/Header Files/EngineStrings.h +++ b/src/CalcManager/Header Files/EngineStrings.h @@ -13,6 +13,11 @@ * Created: 13-Feb-2008 * \****************************************************************************/ + +#pragma once + +#include + inline constexpr auto IDS_ERRORS_FIRST = 99; // This is the list of error strings corresponding to SCERR_DIVIDEZERO.. diff --git a/src/CalcManager/Header Files/History.h b/src/CalcManager/Header Files/History.h index 4f82088f..7446616d 100644 --- a/src/CalcManager/Header Files/History.h +++ b/src/CalcManager/Header Files/History.h @@ -3,6 +3,7 @@ #pragma once +#include #include "ICalcDisplay.h" #include "IHistoryDisplay.h" #include "Rational.h" diff --git a/src/CalcManager/Header Files/ICalcDisplay.h b/src/CalcManager/Header Files/ICalcDisplay.h index 2ba57e65..73fce932 100644 --- a/src/CalcManager/Header Files/ICalcDisplay.h +++ b/src/CalcManager/Header Files/ICalcDisplay.h @@ -12,7 +12,7 @@ public: virtual void SetPrimaryDisplay(const std::wstring& pszText, bool isError) = 0; virtual void SetIsInError(bool isInError) = 0; virtual void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands) = 0; - virtual void SetParenDisplayText(const std::wstring& pszText) = 0; + virtual void SetParenthesisNumber(_In_ unsigned int count) = 0; virtual void OnNoRightParenAdded() = 0; virtual void MaxDigitsReached() = 0; // not an error but still need to inform UI layer. virtual void BinaryOperatorReceived() = 0; diff --git a/src/CalcManager/Header Files/Number.h b/src/CalcManager/Header Files/Number.h index c67aa108..769e3acf 100644 --- a/src/CalcManager/Header Files/Number.h +++ b/src/CalcManager/Header Files/Number.h @@ -3,6 +3,7 @@ #pragma once +#include #include "Ratpack/ratpak.h" namespace CalcEngine diff --git a/src/CalcManager/Header Files/RationalMath.h b/src/CalcManager/Header Files/RationalMath.h index b52c1c5f..59500573 100644 --- a/src/CalcManager/Header Files/RationalMath.h +++ b/src/CalcManager/Header Files/RationalMath.h @@ -13,6 +13,7 @@ namespace CalcEngine::RationalMath Rational Pow(Rational const& base, Rational const& pow); Rational Root(Rational const& base, Rational const& root); Rational Fact(Rational const& rat); + Rational Mod(Rational const& a, Rational const& b); Rational Exp(Rational const& rat); Rational Log(Rational const& rat); diff --git a/src/CalcManager/Ratpack/basex.cpp b/src/CalcManager/Ratpack/basex.cpp index 97a29dd4..2685af87 100644 --- a/src/CalcManager/Ratpack/basex.cpp +++ b/src/CalcManager/Ratpack/basex.cpp @@ -14,8 +14,8 @@ // internal base is a power of 2. // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" +#include // for memmove void _mulnumx( PNUMBER *pa, PNUMBER b ); diff --git a/src/CalcManager/Ratpack/conv.cpp b/src/CalcManager/Ratpack/conv.cpp index c25d4481..3a1aaa14 100644 --- a/src/CalcManager/Ratpack/conv.cpp +++ b/src/CalcManager/Ratpack/conv.cpp @@ -17,7 +17,10 @@ // //--------------------------------------------------------------------------- -#include "pch.h" +#include +#include +#include +#include // for memmove, memcpy #include "ratpak.h" using namespace std; diff --git a/src/CalcManager/Ratpack/exp.cpp b/src/CalcManager/Ratpack/exp.cpp index c8691cfa..e4939db4 100644 --- a/src/CalcManager/Ratpack/exp.cpp +++ b/src/CalcManager/Ratpack/exp.cpp @@ -14,7 +14,6 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" @@ -408,7 +407,7 @@ void powratNumeratorDenominator(PRAT *px, PRAT y, uint32_t radix, int32_t precis //--------------------------------------------------------------------------- void powratcomp(PRAT *px, PRAT y, uint32_t radix, int32_t precision) { - int32_t sign = ((*px)->pp->sign * (*px)->pq->sign); + int32_t sign = SIGN(*px); // Take the absolute value (*px)->pp->sign = 1; diff --git a/src/CalcManager/Ratpack/fact.cpp b/src/CalcManager/Ratpack/fact.cpp index c974b005..bdcbccb0 100644 --- a/src/CalcManager/Ratpack/fact.cpp +++ b/src/CalcManager/Ratpack/fact.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //----------------------------------------------------------------------------- @@ -13,7 +13,6 @@ // Contains fact(orial) and supporting _gamma functions. // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" @@ -216,7 +215,7 @@ void factrat( PRAT *px, uint32_t radix, int32_t precision) // Check for negative integers and throw an error. if ( ( zerrat(frac) || ( LOGRATRADIX(frac) <= -precision) ) && - ( (*px)->pp->sign * (*px)->pq->sign == -1 ) ) + ( SIGN(*px) == -1 ) ) { throw CALC_E_DOMAIN; } diff --git a/src/CalcManager/Ratpack/itrans.cpp b/src/CalcManager/Ratpack/itrans.cpp index 1ecbf4a1..1521308d 100644 --- a/src/CalcManager/Ratpack/itrans.cpp +++ b/src/CalcManager/Ratpack/itrans.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //----------------------------------------------------------------------------- @@ -15,7 +15,6 @@ // Special Information // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" @@ -92,11 +91,9 @@ void asinanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32 void asinrat( PRAT *px, uint32_t radix, int32_t precision) { - int32_t sgn; PRAT pret= nullptr; PRAT phack= nullptr; - - sgn = (*px)->pp->sign* (*px)->pq->sign; + int32_t sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; @@ -204,9 +201,7 @@ void _acosrat( PRAT *px, int32_t precision) void acosrat( PRAT *px, uint32_t radix, int32_t precision) { - int32_t sgn; - - sgn = (*px)->pp->sign*(*px)->pq->sign; + int32_t sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; @@ -291,10 +286,8 @@ void _atanrat( PRAT *px, int32_t precision) void atanrat( PRAT *px, uint32_t radix, int32_t precision) { - int32_t sgn; PRAT tmpx= nullptr; - - sgn = (*px)->pp->sign * (*px)->pq->sign; + int32_t sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; diff --git a/src/CalcManager/Ratpack/itransh.cpp b/src/CalcManager/Ratpack/itransh.cpp index d6734a1f..50941287 100644 --- a/src/CalcManager/Ratpack/itransh.cpp +++ b/src/CalcManager/Ratpack/itransh.cpp @@ -16,7 +16,6 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" diff --git a/src/CalcManager/Ratpack/logic.cpp b/src/CalcManager/Ratpack/logic.cpp index c7f8a3a3..7939ce26 100644 --- a/src/CalcManager/Ratpack/logic.cpp +++ b/src/CalcManager/Ratpack/logic.cpp @@ -13,59 +13,58 @@ // Contains routines for and, or, xor, not and other support // //--------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" using namespace std; -void lshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void lshrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision) { - PRAT pwr= nullptr; + PRAT pwr = nullptr; int32_t intb; intrat(pa, radix, precision); - if ( !zernum( (*pa)->pp ) ) - { + if (!zernum((*pa)->pp)) + { // If input is zero we're done. - if ( rat_gt( b, rat_max_exp, precision) ) - { + if (rat_gt(b, rat_max_exp, precision)) + { // Don't attempt lsh of anything big - throw( CALC_E_DOMAIN ); - } + throw(CALC_E_DOMAIN); + } intb = rattoi32(b, radix, precision); - DUPRAT(pwr,rat_two); + DUPRAT(pwr, rat_two); ratpowi32(&pwr, intb, precision); mulrat(pa, pwr, precision); destroyrat(pwr); - } + } } -void rshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void rshrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision) { - PRAT pwr= nullptr; + PRAT pwr = nullptr; int32_t intb; intrat(pa, radix, precision); - if ( !zernum( (*pa)->pp ) ) - { + if (!zernum((*pa)->pp)) + { // If input is zero we're done. - if ( rat_lt( b, rat_min_exp, precision) ) - { + if (rat_lt(b, rat_min_exp, precision)) + { // Don't attempt rsh of anything big and negative. - throw( CALC_E_DOMAIN ); - } + throw(CALC_E_DOMAIN); + } intb = rattoi32(b, radix, precision); - DUPRAT(pwr,rat_two); + DUPRAT(pwr, rat_two); ratpowi32(&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 ); +void boolrat(PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision); +void boolnum(PNUMBER *pa, PNUMBER b, int func); enum { @@ -74,22 +73,22 @@ enum { FUNC_XOR } BOOL_FUNCS; -void andrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void andrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision) { - boolrat( pa, b, FUNC_AND, radix, precision); + boolrat(pa, b, FUNC_AND, radix, precision); } -void orrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void orrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision) { - boolrat( pa, b, FUNC_OR, radix, precision); + boolrat(pa, b, FUNC_OR, radix, precision); } -void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void xorrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision) { - boolrat( pa, b, FUNC_XOR, radix, precision); + boolrat(pa, b, FUNC_XOR, radix, precision); } //--------------------------------------------------------------------------- @@ -104,15 +103,15 @@ void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) // //--------------------------------------------------------------------------- -void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision) +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); + PRAT tmp = nullptr; + intrat(pa, radix, precision); + DUPRAT(tmp, b); + intrat(&tmp, radix, precision); - boolnum( &((*pa)->pp), tmp->pp, func ); + boolnum(&((*pa)->pp), tmp->pp, func); destroyrat(tmp); } @@ -130,11 +129,11 @@ void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision) // //--------------------------------------------------------------------------- -void boolnum( PNUMBER *pa, PNUMBER b, int func ) +void boolnum(PNUMBER *pa, PNUMBER b, int func) { - PNUMBER c= nullptr; - PNUMBER a= nullptr; + PNUMBER c = nullptr; + PNUMBER a = nullptr; MANTTYPE *pcha; MANTTYPE *pchb; MANTTYPE *pchc; @@ -143,26 +142,26 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func ) 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 ); + 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++ ) + 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) { - 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; @@ -172,15 +171,51 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func ) case FUNC_XOR: *pchc++ = da ^ db; break; - } } + } c->sign = a->sign; - while ( c->cdigit > 1 && *(--pchc) == 0 ) - { + while (c->cdigit > 1 && *(--pchc) == 0) + { c->cdigit--; - } - destroynum( *pa ); - *pa=c; + } + destroynum(*pa); + *pa = c; +} + +//----------------------------------------------------------------------------- +// +// FUNCTION: remrat +// +// ARGUMENTS: pointer to a rational a second rational. +// +// RETURN: None, changes pointer. +// +// DESCRIPTION: Calculate the remainder of *pa / b, +// equivalent of 'pa % b' in C/C++ and produces a result +// that is either zero or has the same sign as the dividend. +// +//----------------------------------------------------------------------------- + +void remrat(PRAT *pa, PRAT b) + +{ + if (zerrat(b)) + { + throw CALC_E_INDEFINITE; + } + + PRAT tmp = nullptr; + 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); } //----------------------------------------------------------------------------- @@ -191,28 +226,38 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func ) // // RETURN: None, changes pointer. // -// DESCRIPTION: Does the rational equivalent of frac(*pa); +// DESCRIPTION: Calculate the remainder of *pa / b, with the sign of the result +// either zero or has the same sign as the divisor. +// NOTE: When *pa or b are negative, the result won't be the same as +// the C/C++ operator %, use remrat if it's the behavior you expect. // //----------------------------------------------------------------------------- -void modrat( PRAT *pa, PRAT b ) - +void modrat(PRAT *pa, PRAT b) { + //contrary to remrat(X, 0) returning 0, modrat(X, 0) must return X + if (zerrat(b)) + { + return; + } + PRAT tmp = nullptr; + DUPRAT(tmp, b); - if ( zerrat( b ) ) - { - throw CALC_E_INDEFINITE; - } - DUPRAT(tmp,b); + auto needAdjust = (SIGN(*pa) == -1 ? (SIGN(b) == 1) : (SIGN(b) == -1)); - mulnumx( &((*pa)->pp), tmp->pq ); - mulnumx( &(tmp->pp), (*pa)->pq ); - remnum( &((*pa)->pp), tmp->pp, BASEX ); - mulnumx( &((*pa)->pq), tmp->pq ); + mulnumx(&((*pa)->pp), tmp->pq); + mulnumx(&(tmp->pp), (*pa)->pq); + remnum(&((*pa)->pp), tmp->pp, BASEX); + mulnumx(&((*pa)->pq), tmp->pq); + + if (needAdjust && !zerrat(*pa)) + { + addrat(pa, b, BASEX); + } // Get *pa back in the integer over integer form. RENORMALIZE(*pa); - destroyrat( tmp ); + destroyrat(tmp); } diff --git a/src/CalcManager/Ratpack/num.cpp b/src/CalcManager/Ratpack/num.cpp index 2bfa6b89..7b45fea0 100644 --- a/src/CalcManager/Ratpack/num.cpp +++ b/src/CalcManager/Ratpack/num.cpp @@ -17,7 +17,8 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" +#include +#include // for memmove #include "ratpak.h" using namespace std; diff --git a/src/CalcManager/Ratpack/rat.cpp b/src/CalcManager/Ratpack/rat.cpp index e101458a..e5a6d254 100644 --- a/src/CalcManager/Ratpack/rat.cpp +++ b/src/CalcManager/Ratpack/rat.cpp @@ -16,7 +16,6 @@ // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" using namespace std; diff --git a/src/CalcManager/Ratpack/ratpak.h b/src/CalcManager/Ratpack/ratpak.h index 75ac28de..349569a6 100644 --- a/src/CalcManager/Ratpack/ratpak.h +++ b/src/CalcManager/Ratpack/ratpak.h @@ -17,7 +17,11 @@ // //----------------------------------------------------------------------------- +#include +#include #include "CalcErr.h" +#include // for memmove +#include // for SAL static constexpr uint32_t BASEXPWR = 31L;// Internal log2(BASEX) static constexpr uint32_t BASEX = 0x80000000; // Internal radix used in calculations, hope to raise @@ -148,6 +152,9 @@ extern PRAT rat_min_i32; #define LOGNUM2(pnum) ((pnum)->cdigit+(pnum)->exp) #define LOGRAT2(prat) (LOGNUM2((prat)->pp)-LOGNUM2((prat)->pq)) +// SIGN returns the sign of the rational +#define SIGN(prat) ((prat)->pp->sign*(prat)->pq->sign) + #if defined( DEBUG_RATPAK ) //----------------------------------------------------------------------------- // @@ -423,7 +430,8 @@ 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 remrat(_Inout_ PRAT *pa, _In_ PRAT b); +extern void modrat(_Inout_ PRAT *pa, _In_ PRAT b); extern void gcdrat( _Inout_ PRAT *pa, 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); diff --git a/src/CalcManager/Ratpack/support.cpp b/src/CalcManager/Ratpack/support.cpp index ebee00c6..1e6a9204 100644 --- a/src/CalcManager/Ratpack/support.cpp +++ b/src/CalcManager/Ratpack/support.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //---------------------------------------------------------------------------- @@ -18,7 +18,9 @@ // //---------------------------------------------------------------------------- -#include "pch.h" +#include +#include // for memmove +#include // for wostream #include "ratpak.h" using namespace std; @@ -296,7 +298,7 @@ void intrat( PRAT *px, uint32_t radix, int32_t precision) // Subtract the fractional part of the rational PRAT pret = nullptr; DUPRAT(pret,*px); - modrat( &pret, rat_one ); + remrat( &pret, rat_one ); subrat( px, pret, precision); destroyrat( pret ); @@ -348,8 +350,7 @@ bool rat_ge( PRAT a, PRAT b, int32_t precision) b->pp->sign *= -1; addrat( &rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( zernum( rattmp->pp ) || - rattmp->pp->sign * rattmp->pq->sign == 1 ); + bool bret = ( zernum( rattmp->pp ) || SIGN(rattmp) == 1 ); destroyrat( rattmp ); return( bret ); } @@ -374,8 +375,7 @@ bool rat_gt( PRAT a, PRAT b, int32_t precision) b->pp->sign *= -1; addrat( &rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( !zernum( rattmp->pp ) && - rattmp->pp->sign * rattmp->pq->sign == 1 ); + bool bret = ( !zernum( rattmp->pp ) && SIGN(rattmp) == 1 ); destroyrat( rattmp ); return( bret ); } @@ -400,8 +400,7 @@ bool rat_le( PRAT a, PRAT b, int32_t precision) b->pp->sign *= -1; addrat( &rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( zernum( rattmp->pp ) || - rattmp->pp->sign * rattmp->pq->sign == -1 ); + bool bret = ( zernum( rattmp->pp ) || SIGN(rattmp) == -1 ); destroyrat( rattmp ); return( bret ); } @@ -426,8 +425,7 @@ bool rat_lt( PRAT a, PRAT b, int32_t precision) b->pp->sign *= -1; addrat( &rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( !zernum( rattmp->pp ) && - rattmp->pp->sign * rattmp->pq->sign == -1 ); + bool bret = ( !zernum( rattmp->pp ) && SIGN(rattmp) == -1 ); destroyrat( rattmp ); return( bret ); } diff --git a/src/CalcManager/Ratpack/trans.cpp b/src/CalcManager/Ratpack/trans.cpp index 82897564..3706d44d 100644 --- a/src/CalcManager/Ratpack/trans.cpp +++ b/src/CalcManager/Ratpack/trans.cpp @@ -14,7 +14,6 @@ // //---------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" diff --git a/src/CalcManager/Ratpack/transh.cpp b/src/CalcManager/Ratpack/transh.cpp index 92a12583..85c27f8b 100644 --- a/src/CalcManager/Ratpack/transh.cpp +++ b/src/CalcManager/Ratpack/transh.cpp @@ -14,7 +14,6 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" diff --git a/src/CalcManager/UnitConverter.cpp b/src/CalcManager/UnitConverter.cpp index 0548719a..7bfc4817 100644 --- a/src/CalcManager/UnitConverter.cpp +++ b/src/CalcManager/UnitConverter.cpp @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include +#include +#include // for std::sort #include "Command.h" #include "UnitConverter.h" @@ -63,7 +65,8 @@ UnitConverter::UnitConverter(_In_ const shared_ptr& dataLo unquoteConversions[L"{sc}"] = L';'; unquoteConversions[L"{lb}"] = LEFTESCAPECHAR; unquoteConversions[L"{rb}"] = RIGHTESCAPECHAR; - Reset(); + ClearValues(); + ResetCategoriesAndRatios(); } void UnitConverter::Initialize() @@ -75,7 +78,7 @@ bool UnitConverter::CheckLoad() { if (m_categories.empty()) { - Reset(); + ResetCategoriesAndRatios(); } return !m_categories.empty(); } @@ -152,7 +155,6 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType Calculate(); UpdateCurrencySymbols(); - UpdateViewModel(); } /// @@ -336,7 +338,8 @@ wstring UnitConverter::Serialize() /// wstring holding the serialized data. If it does not have expected number of parameters, we will ignore it void UnitConverter::DeSerialize(const wstring& serializedData) { - Reset(); + ClearValues(); + ResetCategoriesAndRatios(); if (serializedData.empty()) { @@ -403,12 +406,30 @@ void UnitConverter::RestoreUserPreferences(const wstring& userPreferences) } vector outerTokens = StringToVector(userPreferences, L"|"); - if (outerTokens.size() == 3) + if (outerTokens.size() != 3) { - m_fromType = StringToUnit(outerTokens[0]); - m_toType = StringToUnit(outerTokens[1]); - m_currentCategory = StringToCategory(outerTokens[2]); + return; } + + auto fromType = StringToUnit(outerTokens[0]); + auto toType = StringToUnit(outerTokens[1]); + m_currentCategory = StringToCategory(outerTokens[2]); + + // Only restore from the saved units if they are valid in the current available units. + auto itr = m_categoryToUnits.find(m_currentCategory); + if (itr != m_categoryToUnits.end()) + { + const auto& curUnits = itr->second; + if (find(curUnits.begin(), curUnits.end(), fromType) != curUnits.end()) + { + m_fromType = fromType; + } + if (find(curUnits.begin(), curUnits.end(), toType) != curUnits.end()) + { + m_toType = toType; + } + } + } /// @@ -615,7 +636,7 @@ void UnitConverter::SendCommand(Command command) clearFront = false; clearBack = false; ClearValues(); - Reset(); + ResetCategoriesAndRatios(); break; default: @@ -634,8 +655,6 @@ void UnitConverter::SendCommand(Command command) } Calculate(); - - UpdateViewModel(); } /// @@ -824,19 +843,16 @@ vector> UnitConverter::CalculateSuggested() returnVector.push_back(whimsicalReturnVector.at(0)); } - // - return returnVector; } /// -/// Resets the converter to its initial state +/// Resets categories and ratios /// -void UnitConverter::Reset() +void UnitConverter::ResetCategoriesAndRatios() { m_categories = m_dataLoader->LoadOrderedCategories(); - ClearValues(); m_switchedActive = false; if (m_categories.empty()) @@ -881,7 +897,6 @@ void UnitConverter::Reset() } InitializeSelectedUnits(); - Calculate(); } /// @@ -972,11 +987,21 @@ bool UnitConverter::AnyUnitIsEmpty() /// void UnitConverter::Calculate() { - unordered_map conversionTable = m_ratioMap[m_fromType]; - double returnValue = stod(m_currentDisplay); - if (AnyUnitIsEmpty() || (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0)) + if (AnyUnitIsEmpty()) { m_returnDisplay = m_currentDisplay; + m_returnHasDecimal = m_currentHasDecimal; + TrimString(m_returnDisplay); + UpdateViewModel(); + return; + } + + unordered_map conversionTable = m_ratioMap[m_fromType]; + double returnValue = stod(m_currentDisplay); + if (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0) + { + m_returnDisplay = m_currentDisplay; + m_returnHasDecimal = m_currentHasDecimal; TrimString(m_returnDisplay); } else @@ -1015,9 +1040,9 @@ void UnitConverter::Calculate() m_returnDisplay = returnString; TrimString(m_returnDisplay); } + m_returnHasDecimal = (m_returnDisplay.find(L'.') != m_returnDisplay.npos); } - - m_returnHasDecimal = (m_returnDisplay.find(L'.') != m_returnDisplay.npos); + UpdateViewModel(); } /// diff --git a/src/CalcManager/UnitConverter.h b/src/CalcManager/UnitConverter.h index 4e130d12..4c77f8aa 100644 --- a/src/CalcManager/UnitConverter.h +++ b/src/CalcManager/UnitConverter.h @@ -1,8 +1,14 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once +#include +#include +#include +#include // for SAL +#include // for std::shared_ptr + namespace UnitConversionManager { enum class Command; @@ -195,6 +201,8 @@ namespace UnitConversionManager virtual void SetViewModelCallback(_In_ const std::shared_ptr& newCallback) = 0; virtual void SetViewModelCurrencyCallback(_In_ const std::shared_ptr& newCallback) = 0; virtual concurrency::task> RefreshCurrencyRatios() = 0; + virtual void Calculate() = 0; + virtual void ResetCategoriesAndRatios() = 0; }; class UnitConverter : public IUnitConverter, public std::enable_shared_from_this @@ -218,6 +226,8 @@ namespace UnitConversionManager void SetViewModelCallback(_In_ const std::shared_ptr& newCallback) override; void SetViewModelCurrencyCallback(_In_ const std::shared_ptr& newCallback) override; concurrency::task> RefreshCurrencyRatios() override; + void Calculate() override; + void ResetCategoriesAndRatios() override; // IUnitConverter static std::vector StringToVector(const std::wstring& w, const wchar_t * delimiter, bool addRemainder = false); @@ -228,9 +238,7 @@ namespace UnitConversionManager bool CheckLoad(); double Convert(double value, ConversionData conversionData); std::vector> CalculateSuggested(); - void Reset(); void ClearValues(); - void Calculate(); void TrimString(std::wstring& input); void InitializeSelectedUnits(); std::wstring RoundSignificant(double num, int numSignificant); diff --git a/src/CalcManager/pch.cpp b/src/CalcManager/pch.cpp index 1da170eb..8ca9987d 100644 --- a/src/CalcManager/pch.cpp +++ b/src/CalcManager/pch.cpp @@ -1,4 +1,6 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +// Intentionally do not include the pch.h here. For projects that don't +// use precompiled headers, including the header here would force unnecessary compilation. +// The pch will be included through forced include. diff --git a/src/CalcManager/pch.h b/src/CalcManager/pch.h index c5f39b6d..e66e1a38 100644 --- a/src/CalcManager/pch.h +++ b/src/CalcManager/pch.h @@ -3,27 +3,20 @@ #pragma once -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif +// The CalcManager project should be able to be compiled with or without a precompiled header +// in - order to support other toolchains besides MSVC. When adding new system headers, make sure +// that the relevant source file includes all headers it needs, but then also add the system headers +// here so that MSVC users see the performance benefit. -// Windows headers define min/max macros. -// Disable it for project code. -#define NOMINMAX - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include diff --git a/src/CalcManager/targetver.h b/src/CalcManager/targetver.h deleted file mode 100644 index 221efabb..00000000 --- a/src/CalcManager/targetver.h +++ /dev/null @@ -1,11 +0,0 @@ -// 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 diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index f99ce036..5033080b 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/src/CalcViewModel/ApplicationViewModel.cpp @@ -36,7 +36,6 @@ namespace { StringReference CategoriesPropertyName(L"Categories"); StringReference ClearMemoryVisibilityPropertyName(L"ClearMemoryVisibility"); - StringReference AppBarVisibilityPropertyName(L"AppBarVisibility"); } ApplicationViewModel::ApplicationViewModel() : @@ -164,7 +163,6 @@ void ApplicationViewModel::OnModeChanged() TraceLogger::GetInstance().LogModeChangeEnd(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); RaisePropertyChanged(ClearMemoryVisibilityPropertyName); - RaisePropertyChanged(AppBarVisibilityPropertyName); } void ApplicationViewModel::OnCopyCommand(Object^ parameter) @@ -189,7 +187,7 @@ void ApplicationViewModel::OnPasteCommand(Object^ parameter) { ConverterViewModel->OnPasteCommand(parameter); } - else + else if (NavCategory::IsCalculatorViewMode(m_mode)) { CalculatorViewModel->OnPasteCommand(parameter); } diff --git a/src/CalcViewModel/ApplicationViewModel.h b/src/CalcViewModel/ApplicationViewModel.h index f13e00ad..74567ca1 100644 --- a/src/CalcViewModel/ApplicationViewModel.h +++ b/src/CalcViewModel/ApplicationViewModel.h @@ -66,16 +66,6 @@ namespace CalculatorApp } } - 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(); diff --git a/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp b/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp index 4dc48074..b17aaba4 100644 --- a/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp +++ b/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // Implementation of the NarratorNotifier class. diff --git a/src/CalcViewModel/Common/CalculatorDisplay.cpp b/src/CalcViewModel/Common/CalculatorDisplay.cpp index cd531e01..4da98129 100644 --- a/src/CalcViewModel/Common/CalculatorDisplay.cpp +++ b/src/CalcViewModel/Common/CalculatorDisplay.cpp @@ -36,7 +36,7 @@ void CalculatorDisplay::SetPrimaryDisplay(_In_ const wstring& displayStringValue } } -void CalculatorDisplay::SetParenDisplayText(_In_ const std::wstring& parenthesisCount) +void CalculatorDisplay::SetParenthesisNumber(_In_ unsigned int parenthesisCount) { if (m_callbackReference != nullptr) { diff --git a/src/CalcViewModel/Common/CalculatorDisplay.h b/src/CalcViewModel/Common/CalculatorDisplay.h index 9ff06a6e..877bdf06 100644 --- a/src/CalcViewModel/Common/CalculatorDisplay.h +++ b/src/CalcViewModel/Common/CalculatorDisplay.h @@ -21,7 +21,7 @@ namespace CalculatorApp void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands) override; void SetMemorizedNumbers(_In_ const std::vector& memorizedNumbers) override; void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) override; - void SetParenDisplayText(_In_ const std::wstring& parenthesisCount) override; + void SetParenthesisNumber(_In_ unsigned int parenthesisCount) override; void OnNoRightParenAdded() override; void MaxDigitsReached() override; void BinaryOperatorReceived() override; diff --git a/src/CalcViewModel/Common/CopyPasteManager.cpp b/src/CalcViewModel/Common/CopyPasteManager.cpp index d10c4c57..6d6807ba 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.cpp +++ b/src/CalcViewModel/Common/CopyPasteManager.cpp @@ -15,14 +15,15 @@ using namespace Windows::Foundation; using namespace Windows::System; using namespace Windows::ApplicationModel::DataTransfer; -unsigned long long maxOperandNumber; - String^ CopyPasteManager::supportedFormats[] = { StandardDataFormats::Text }; -constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" }; +static constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" }; + +// The below values can not be "constexpr"-ed, +// as both wstring_view and wchar[] can not be concatenated // [\s\x85] means white-space characters static const wstring c_wspc = L"[\\s\\x85]*"; static const wstring c_wspcLParens = c_wspc + L"[(]*" + c_wspc; @@ -103,19 +104,16 @@ task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupTyp int CopyPasteManager::ClipboardTextFormat() { - int result = -1; - - auto dataPackageView = Clipboard::GetContent(); + const auto dataPackageView = Clipboard::GetContent(); for (int i = 0; i < RTL_NUMBER_OF(supportedFormats); i++) { if (dataPackageView->Contains(supportedFormats[i])) { - result = i; - break; + return i; } } - return result; + return -1; } String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType) @@ -268,13 +266,8 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m return false; } - bool expMatched = true; vector patterns{}; - - pair 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()); @@ -292,11 +285,14 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m patterns.assign(unitConverterPatterns.begin(), unitConverterPatterns.end()); } - for (const wstring& operand : operands) + const auto [maxOperandLength, maxOperandValue] = GetMaxOperandLengthAndValue(mode, modeType, programmerNumberBase, bitLengthType); + bool expMatched = true; + + for (const auto& operand : operands) { // Each operand only needs to match one of the available patterns. bool operandMatched = false; - for (const wregex& pattern : patterns) + for (const auto& pattern : patterns) { operandMatched = operandMatched || regex_match(operand, pattern); } @@ -305,7 +301,7 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m { // 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); + const wstring operandValue = SanitizeOperand(operand); // If an operand exceeds the maximum length allowed, break and return. if (OperandLength(operandValue, mode, modeType, programmerNumberBase) > maxOperandLength) @@ -341,16 +337,16 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) { - size_t maxLength = 0; - uint64_t maxValue = 0; - + constexpr size_t defaultMaxOperandLength = 0; + constexpr uint64_t defaultMaxValue = 0; + if (mode == ViewMode::Standard) { - maxLength = MaxStandardOperandLength; + return make_pair(MaxStandardOperandLength, defaultMaxValue); } else if (mode == ViewMode::Scientific) { - maxLength = MaxScientificOperandLength; + return make_pair(MaxScientificOperandLength, defaultMaxValue); } else if (mode == ViewMode::Programmer) { @@ -390,15 +386,17 @@ pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mo unsigned int signBit = (programmerNumberBase == DecBase) ? 1 : 0; - maxLength = (size_t)ceil((bitLength - signBit) / bitsPerDigit); - maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit)); + const auto maxLength = static_cast(ceil((bitLength - signBit) / bitsPerDigit)); + const uint64_t maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit)); + + return make_pair(maxLength, maxValue); } else if (modeType == CategoryGroupType::Converter) { - maxLength = MaxConverterInputLength; + return make_pair(MaxConverterInputLength, defaultMaxValue); } - return make_pair(maxLength, maxValue); + return make_pair(defaultMaxOperandLength, defaultMaxValue); } wstring CopyPasteManager::SanitizeOperand(const wstring& operand) @@ -417,8 +415,7 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u return false; } - // Default to base10 - int intBase = 10; + int intBase; switch (numberBase) { case HexBase: @@ -430,6 +427,7 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u case BinBase: intBase = 2; break; + default: case DecBase: intBase = 10; break; @@ -441,11 +439,11 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u result = stoull(operand, &size, intBase); return true; } - catch (invalid_argument) + catch (const invalid_argument&) { // Do nothing } - catch (out_of_range) + catch (const out_of_range&) { // Do nothing } @@ -453,35 +451,28 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u 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(); +size_t CopyPasteManager::OperandLength(const wstring& operand, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase) +{ + if (modeType == CategoryGroupType::Converter) { + return operand.length(); } - return len; + switch(mode) { + case ViewMode::Standard: + case ViewMode::Scientific: + return StandardScientificOperandLength(operand); + + case ViewMode::Programmer: + return ProgrammerOperandLength(operand, programmerNumberBase); + + default: + return 0; + } } -size_t CopyPasteManager::StandardScientificOperandLength(wstring operand) -{ - bool hasDecimal = false; - for (size_t i = 0; i < operand.length(); i++) - { - if (operand[i] == L'.') - { - hasDecimal = true; - } - } +size_t CopyPasteManager::StandardScientificOperandLength(const wstring& operand) +{ + const bool hasDecimal = operand.find('.') != wstring::npos; if (hasDecimal) { @@ -503,8 +494,7 @@ size_t CopyPasteManager::StandardScientificOperandLength(wstring operand) size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int numberBase) { - size_t len = operand.length(); - + vector prefixes{}; vector suffixes{}; switch (numberBase) @@ -525,7 +515,7 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num break; default: // No defined prefixes/suffixes - break; + return 0; } // UInt suffixes are common across all modes @@ -535,9 +525,11 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num wstring operandUpper = operand; transform(operandUpper.begin(), operandUpper.end(), operandUpper.begin(), towupper); + size_t len = operand.length(); + // 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) + for (const auto& suffix : suffixes) { if (len < suffix.length()) { @@ -552,7 +544,7 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num } // Detect if there is a prefix and subtract its length - for (const wstring& prefix : prefixes) + for (const auto& prefix : prefixes) { if (len < prefix.length()) { diff --git a/src/CalcViewModel/Common/CopyPasteManager.h b/src/CalcViewModel/Common/CopyPasteManager.h index 9a886154..9d6fd669 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.h +++ b/src/CalcViewModel/Common/CopyPasteManager.h @@ -13,15 +13,14 @@ namespace CalculatorUnitTests 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 + inline constexpr auto QwordType = 1; + inline constexpr auto DwordType = 2; + inline constexpr auto WordType = 3; + inline constexpr auto ByteType = 4; + inline constexpr auto HexBase = 5; + inline constexpr auto DecBase = 6; + inline constexpr auto OctBase = 7; + inline constexpr auto BinBase = 8; class CopyPasteManager { @@ -55,8 +54,8 @@ namespace CalculatorApp static std::pair 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 OperandLength(const std::wstring& operand, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1); + static size_t StandardScientificOperandLength(const std::wstring& operand); static size_t ProgrammerOperandLength(const std::wstring& operand, int numberBase); static std::wstring RemoveUnwantedCharsFromWstring(const std::wstring& input); diff --git a/src/CalcViewModel/Common/DateCalculator.cpp b/src/CalcViewModel/Common/DateCalculator.cpp index 63efcdd0..4ccba38c 100644 --- a/src/CalcViewModel/Common/DateCalculator.cpp +++ b/src/CalcViewModel/Common/DateCalculator.cpp @@ -20,13 +20,27 @@ DateCalculationEngine::DateCalculationEngine(_In_ String^ calendarIdentifier) // 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) { + auto currentCalendarSystem = m_calendar->GetCalendarSystem(); + try { m_calendar->SetDateTime(startDate); if (duration.year != 0) { + // The Japanese Era system can have multiple year partitions within the same year. + // For example, April 30, 2019 is denoted April 30, Heisei 31; May 1, 2019 is denoted as May 1, Reiwa 1. + // The Calendar treats Heisei 31 and Reiwa 1 as separate years, which results in some unexpected behaviors where subtracting a year from Reiwa 1 results in a date in Heisei 31. + // To provide the expected result across era boundaries, we first convert the Japanese era system to a Gregorian system, do date math, and then convert back to the Japanese era system. + // This works because the Japanese era system maintains the same year/month boundaries and durations as the Gregorian system and is only different in display value. + if (currentCalendarSystem == CalendarIdentifiers::Japanese) + { + m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); + } + m_calendar->AddYears(duration.year); + + m_calendar->ChangeCalendarSystem(currentCalendarSystem); } if (duration.month != 0) { @@ -41,6 +55,9 @@ bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const Date } catch (Platform::InvalidArgumentException^ ex) { + // ensure that we revert to the correct calendar system + m_calendar->ChangeCalendarSystem(currentCalendarSystem); + // Do nothing return false; } @@ -52,6 +69,8 @@ bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const 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) { + auto currentCalendarSystem = m_calendar->GetCalendarSystem(); + // For Subtract the Algorithm is different than Add. Here the smaller units are subtracted first // and then the larger units. try @@ -68,13 +87,28 @@ bool DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ const } if (duration.year != 0) { + // The Japanese Era system can have multiple year partitions within the same year. + // For example, April 30, 2019 is denoted April 30, Heisei 31; May 1, 2019 is denoted as May 1, Reiwa 1. + // The Calendar treats Heisei 31 and Reiwa 1 as separate years, which results in some unexpected behaviors where subtracting a year from Reiwa 1 results in a date in Heisei 31. + // To provide the expected result across era boundaries, we first convert the Japanese era system to a Gregorian system, do date math, and then convert back to the Japanese era system. + // This works because the Japanese era system maintains the same year/month boundaries and durations as the Gregorian system and is only different in display value. + if (currentCalendarSystem == CalendarIdentifiers::Japanese) + { + m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); + } + m_calendar->AddYears(-duration.year); + + m_calendar->ChangeCalendarSystem(currentCalendarSystem); } *endDate = m_calendar->GetDateTime(); } catch (Platform::InvalidArgumentException^ ex) { + // ensure that we revert to the correct calendar system + m_calendar->ChangeCalendarSystem(currentCalendarSystem); + // Do nothing return false; } diff --git a/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h b/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h index bd8cb169..7032b250 100644 --- a/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h +++ b/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace CalculatorApp @@ -162,7 +162,8 @@ namespace CalculatorApp Data_Zebibytes = UnitStart + 162, Data_Zetabits = UnitStart + 163, Data_Zetabytes = UnitStart + 164, - UnitEnd = Data_Zetabytes + Area_Pyeong = UnitStart + 165, + UnitEnd = Area_Pyeong }; } } diff --git a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp index 45411c1d..c95fda5a 100644 --- a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp @@ -118,12 +118,19 @@ void UnitConverterDataLoader::LoadData() unordered_map unitConversions = categoryToUnitConversionDataMap.at(categoryViewMode); double unitFactor = unitConversions[unit.id]; - for (auto itr = unitConversions.begin(); itr != unitConversions.end(); ++itr) + for (const auto&[id, conversionFactor] : unitConversions) { + if (idToUnit.find(id) == idToUnit.end()) + { + // Optional units will not be in idToUnit but can be in unitConversions. + // For optional units that did not make it to the current set of units, just continue. + continue; + } + UCM::ConversionData parsedData = { 1.0, 0.0, false }; - assert(itr->second > 0); // divide by zero assert - parsedData.ratio = unitFactor / itr->second; - conversions.insert(pair(idToUnit.at(itr->first), parsedData)); + assert(conversionFactor > 0); // divide by zero assert + parsedData.ratio = unitFactor / conversionFactor; + conversions.insert(pair(idToUnit.at(id), parsedData)); } } else @@ -175,6 +182,10 @@ void UnitConverterDataLoader::GetUnits(_In_ unordered_map areaUnits; areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Acre, GetLocalizedStringName(L"UnitName_Acre"), GetLocalizedStringName(L"UnitAbbreviation_Acre"), 9 }); areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Hectare, GetLocalizedStringName(L"UnitName_Hectare"), GetLocalizedStringName(L"UnitAbbreviation_Hectare"), 4 }); @@ -190,6 +201,10 @@ void UnitConverterDataLoader::GetUnits(_In_ unordered_map dataUnits; @@ -384,6 +399,7 @@ void UnitConverterDataLoader::GetConversionData(_In_ unordered_map()), m_MemorizedNumbers(ref new Vector()), m_IsMemoryEmpty(true), m_IsFToEChecked(false), @@ -80,8 +80,8 @@ StandardCalculatorViewModel::StandardCalculatorViewModel() : m_isBinaryBitFlippingEnabled(false), m_CurrentRadixType(RADIX_TYPE::DEC_RADIX), m_CurrentAngleType(NumbersAndOperatorsEnum::Degree), - m_OpenParenthesisCount(L""), m_Announcement(nullptr), + m_OpenParenthesisCount(0), m_feedbackForButtonPress(nullptr), m_isRtlLanguage(false), m_localizedMaxDigitsReachedAutomationFormat(nullptr), @@ -102,7 +102,6 @@ StandardCalculatorViewModel::StandardCalculatorViewModel() : m_localizedDecimalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::DecButton); m_localizedOctalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::OctButton); m_localizedBinaryAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::BinButton); - m_leftParenthesisAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::LeftParenthesisAutomationFormat); // Initialize the Automation Name CalculationResultAutomationName = GetLocalizedStringFormat(m_localizedCalculationResultAutomationFormat, m_DisplayValue); @@ -216,19 +215,23 @@ void StandardCalculatorViewModel::DisplayPasteError() m_standardCalculatorManager.DisplayPasteError(); } -void StandardCalculatorViewModel::SetParenthesisCount(_In_ const wstring& parenthesisCount) +void StandardCalculatorViewModel::SetParenthesisCount(_In_ unsigned int parenthesisCount) { + if (m_OpenParenthesisCount == parenthesisCount) + { + return; + } + + OpenParenthesisCount = parenthesisCount; if (IsProgrammer || IsScientific) { - OpenParenthesisCount = ref new String(parenthesisCount.c_str()); - RaisePropertyChanged("LeftParenthesisAutomationName"); + SetOpenParenthesisCountNarratorAnnouncement(); } } void StandardCalculatorViewModel::SetOpenParenthesisCountNarratorAnnouncement() { - String^ parenthesisCount = ((m_OpenParenthesisCount == nullptr) ? "0" : m_OpenParenthesisCount); - wstring localizedParenthesisCount = parenthesisCount->Data(); + wstring localizedParenthesisCount = to_wstring(m_OpenParenthesisCount).c_str(); LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedParenthesisCount); String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( @@ -281,15 +284,6 @@ void StandardCalculatorViewModel::DisableButtons(CommandType selectedExpressionC } } -String ^ StandardCalculatorViewModel::GetLeftParenthesisAutomationName() -{ - String^ parenthesisCount = ((m_OpenParenthesisCount == nullptr) ? "0" : m_OpenParenthesisCount); - wstring localizedParenthesisCount = std::wstring(parenthesisCount->Data()); - LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedParenthesisCount); - - return GetLocalizedStringFormat(m_leftParenthesisAutomationFormat, ref new String(localizedParenthesisCount.c_str())); -} - void StandardCalculatorViewModel::SetExpressionDisplay(_Inout_ shared_ptr>> const &tokens, _Inout_ shared_ptr>> const &commands) { m_tokens = tokens; @@ -321,59 +315,67 @@ void StandardCalculatorViewModel::SetTokens(_Inout_ shared_ptr(); - } - else - { - m_ExpressionTokens->Clear(); - } - unsigned int nTokens = 0; tokens->GetSize(&nTokens); + + if (nTokens == 0) + { + m_ExpressionTokens->Clear(); + return; + } + pair currentToken; const auto& localizer = LocalizationSettings::GetInstance(); + const wstring separator = L" "; for (unsigned int i = 0; i < nTokens; ++i) { if (SUCCEEDED(tokens->GetAt(i, ¤tToken))) { Common::TokenType type; - const wstring separator = L" "; bool isEditable = (currentToken.second == -1) ? false : true; localizer.LocalizeDisplayValue(&(currentToken.first)); if (!isEditable) { - if (currentToken.first == separator) - { - type = TokenType::Separator; - } - else - { - type = TokenType::Operator; - } + type = currentToken.first == separator ? TokenType::Separator : TokenType::Operator; } - else { shared_ptr command; IFTPlatformException(m_commands->GetAt(static_cast(currentToken.second), &command)); + type = command->GetCommandType() == CommandType::OperandCommand ? TokenType::Operand : TokenType::Operator; + } - if (command->GetCommandType() == CommandType::OperandCommand) + auto currentTokenString = ref new String(currentToken.first.c_str()); + if (i < m_ExpressionTokens->Size) + { + auto existingItem = m_ExpressionTokens->GetAt(i); + if (type == existingItem->Type && existingItem->Token->Equals(currentTokenString)) { - type = TokenType::Operand; + existingItem->TokenPosition = i; + existingItem->IsTokenEditable = isEditable; + existingItem->CommandIndex = 0; } else { - type = TokenType::Operator; + auto expressionToken = ref new DisplayExpressionToken(currentTokenString, i, isEditable, type); + m_ExpressionTokens->InsertAt(i, expressionToken); } + + } + else + { + auto expressionToken = ref new DisplayExpressionToken(currentTokenString, i, isEditable, type); + m_ExpressionTokens->Append(expressionToken); } - DisplayExpressionToken^ expressionToken = ref new DisplayExpressionToken(ref new String(currentToken.first.c_str()), i, isEditable, type); - m_ExpressionTokens->Append(expressionToken); } } + + while (m_ExpressionTokens->Size != nTokens) + { + m_ExpressionTokens->RemoveAtEnd(); + } } String^ StandardCalculatorViewModel::GetCalculatorExpressionAutomationName() @@ -531,7 +533,7 @@ void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum) { if (commandIndex == 0) { - delete [] temp; + delete[] temp; return; } @@ -552,7 +554,7 @@ void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum) length = m_selectedExpressionLastData->Length() + 1; if (length > 50) { - delete [] temp; + delete[] temp; return; } for (; i < length; ++i) @@ -1422,29 +1424,29 @@ void StandardCalculatorViewModel::SaveEditedCommand(_In_ unsigned int tokenPosit switch (nOpCode) { - case static_cast(Command::CommandASIN) : - updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandSIN), true, angleType); - break; - case static_cast(Command::CommandACOS) : - updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandCOS), true, angleType); - break; - case static_cast(Command::CommandATAN) : - updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandTAN), true, angleType); - break; - case static_cast(Command::CommandASINH) : - updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandSINH), true, angleType); - break; - case static_cast(Command::CommandACOSH) : - updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandCOSH), true, angleType); - break; - case static_cast(Command::CommandATANH) : - updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandTANH), true, angleType); - break; - case static_cast(Command::CommandPOWE) : - updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandLN), true, angleType); - break; - default: - updatedToken = CCalcEngine::OpCodeToUnaryString(nOpCode, false, angleType); + case static_cast(Command::CommandASIN) : + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandSIN), true, angleType); + break; + case static_cast(Command::CommandACOS) : + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandCOS), true, angleType); + break; + case static_cast(Command::CommandATAN) : + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandTAN), true, angleType); + break; + case static_cast(Command::CommandASINH) : + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandSINH), true, angleType); + break; + case static_cast(Command::CommandACOSH) : + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandCOSH), true, angleType); + break; + case static_cast(Command::CommandATANH) : + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandTANH), true, angleType); + break; + case static_cast(Command::CommandPOWE) : + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandLN), true, angleType); + break; + default: + updatedToken = CCalcEngine::OpCodeToUnaryString(nOpCode, false, angleType); } if ((token.first.length() > 0) && (token.first[token.first.length() - 1] == L'(')) { diff --git a/src/CalcViewModel/StandardCalculatorViewModel.h b/src/CalcViewModel/StandardCalculatorViewModel.h index ba6dc82b..f8140a75 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.h +++ b/src/CalcViewModel/StandardCalculatorViewModel.h @@ -48,7 +48,7 @@ namespace CalculatorApp OBSERVABLE_NAMED_PROPERTY_RW(bool, IsInError); OBSERVABLE_PROPERTY_RW(bool, IsOperatorCommand); OBSERVABLE_PROPERTY_RW(Platform::String^, DisplayStringExpression); - OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IVector^, ExpressionTokens); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector^, ExpressionTokens); OBSERVABLE_PROPERTY_RW(Platform::String^, DecimalDisplayValue); OBSERVABLE_PROPERTY_RW(Platform::String^, HexDisplayValue); OBSERVABLE_PROPERTY_RW(Platform::String^, OctalDisplayValue); @@ -75,18 +75,17 @@ namespace CalculatorApp OBSERVABLE_PROPERTY_RW(bool, IsDwordEnabled); OBSERVABLE_PROPERTY_RW(bool, IsWordEnabled); OBSERVABLE_PROPERTY_RW(bool, IsByteEnabled); - OBSERVABLE_NAMED_PROPERTY_RW(Platform::String^, OpenParenthesisCount); OBSERVABLE_PROPERTY_RW(int, CurrentRadixType); OBSERVABLE_PROPERTY_RW(bool, AreTokensUpdated); OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled); OBSERVABLE_PROPERTY_RW(bool, AreProgrammerRadixOperatorsEnabled); OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, Announcement); + OBSERVABLE_PROPERTY_R(unsigned int, OpenParenthesisCount); COMMAND_FOR_METHOD(CopyCommand, StandardCalculatorViewModel::OnCopyCommand); COMMAND_FOR_METHOD(PasteCommand, StandardCalculatorViewModel::OnPasteCommand); COMMAND_FOR_METHOD(ButtonPressed, StandardCalculatorViewModel::OnButtonPressed); COMMAND_FOR_METHOD(ClearMemoryCommand, StandardCalculatorViewModel::OnClearMemoryCommand); - COMMAND_FOR_METHOD(PinUnpinAppBarButtonOnClicked, StandardCalculatorViewModel::OnPinUnpinCommand); COMMAND_FOR_METHOD(MemoryItemPressed, StandardCalculatorViewModel::OnMemoryItemPressed); COMMAND_FOR_METHOD(MemoryAdd, StandardCalculatorViewModel::OnMemoryAdd); COMMAND_FOR_METHOD(MemorySubtract, StandardCalculatorViewModel::OnMemorySubtract); @@ -255,14 +254,6 @@ namespace CalculatorApp void set(bool value) { m_completeTextSelection = value; } } - property Platform::String^ LeftParenthesisAutomationName - { - Platform::String^ get() - { - return GetLeftParenthesisAutomationName(); - } - } - internal: void OnPaste(Platform::String^ pastedString, CalculatorApp::Common::ViewMode mode); void OnCopyCommand(Platform::Object^ parameter); @@ -283,7 +274,7 @@ namespace CalculatorApp void SetTokens(_Inout_ std::shared_ptr>> const &tokens); void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands); void SetHistoryExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands); - void SetParenthesisCount(_In_ const std::wstring& parenthesisCount); + void SetParenthesisCount(_In_ unsigned int parenthesisCount); void SetOpenParenthesisCountNarratorAnnouncement(); void OnNoRightParenAdded(); void SetNoParenAddedNarratorAnnouncement(); @@ -354,7 +345,6 @@ namespace CalculatorApp bool m_isLastOperationHistoryLoad; Platform::String^ m_selectedExpressionLastData; Common::DisplayExpressionToken^ m_selectedExpressionToken; - Platform::String^ m_leftParenthesisAutomationFormat; Platform::String^ LocalizeDisplayValue(_In_ std::wstring const &displayValue, _In_ bool isError); Platform::String^ CalculateNarratorDisplayValue(_In_ std::wstring const &displayValue, _In_ Platform::String^ localizedDisplayValue, _In_ bool isError); @@ -364,7 +354,6 @@ namespace CalculatorApp CalculationManager::Command ConvertToOperatorsEnum(NumbersAndOperatorsEnum operation); void DisableButtons(CalculationManager::CommandType selectedExpressionCommandType); - Platform::String^ GetLeftParenthesisAutomationName(); Platform::String^ m_feedbackForButtonPress; void OnButtonPressed(Platform::Object^ parameter); diff --git a/src/CalcViewModel/UnitConverterViewModel.cpp b/src/CalcViewModel/UnitConverterViewModel.cpp index 76d9aa3a..446ae582 100644 --- a/src/CalcViewModel/UnitConverterViewModel.cpp +++ b/src/CalcViewModel/UnitConverterViewModel.cpp @@ -165,12 +165,16 @@ void UnitConverterViewModel::PopulateData() } void UnitConverterViewModel::OnCategoryChanged(Object^ parameter) +{ + m_model->SendCommand(UCM::Command::Clear); + ResetCategory(); +} + +void UnitConverterViewModel::ResetCategory() { UCM::Category currentCategory = CurrentCategory->GetModelCategory(); IsCurrencyCurrentCategory = currentCategory.id == NavCategory::Serialize(ViewMode::Currency); - m_model->SendCommand(UCM::Command::Clear); - m_isInputBlocked = false; SetSelectedUnits(); @@ -706,7 +710,9 @@ void UnitConverterViewModel::OnCurrencyDataLoadFinished(bool didLoad) { m_isCurrencyDataLoaded = true; CurrencyDataLoadFailed = !didLoad; - ResetView(); + m_model->ResetCategoriesAndRatios(); + m_model->Calculate(); + ResetCategory(); StringReference key = didLoad ? UnitConverterResourceKeys::CurrencyRatesUpdated : UnitConverterResourceKeys::CurrencyRatesUpdateFailed; String^ announcement = AppResourceProvider::GetInstance().GetResourceString(key); diff --git a/src/CalcViewModel/UnitConverterViewModel.h b/src/CalcViewModel/UnitConverterViewModel.h index 5a982384..2c6c8af3 100644 --- a/src/CalcViewModel/UnitConverterViewModel.h +++ b/src/CalcViewModel/UnitConverterViewModel.h @@ -223,6 +223,7 @@ namespace CalculatorApp void UpdateValue2AutomationName(); Platform::String^ Serialize(); void Deserialize(Platform::String^ state); + void ResetCategoriesAndRatio(); // Saving And Restoring User Preferences of Category and Associated-Units across Sessions. void SaveUserPreferences(); @@ -263,6 +264,7 @@ namespace CalculatorApp void RefreshSupplementaryResults(); void UpdateInputBlocked(_In_ const std::wstring& currencyInput); bool UnitsAreValid(); + void ResetCategory(); void OnButtonPressed(Platform::Object^ parameter); Platform::String^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings); diff --git a/src/Calculator.sln b/src/Calculator.sln index 2d36a93f..232fcd1e 100644 --- a/src/Calculator.sln +++ b/src/Calculator.sln @@ -16,6 +16,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcViewModel", "CalcViewMo EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CalculatorUITests", "CalculatorUITests\CalculatorUITests.csproj", "{B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -28,22 +30,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.ActiveCfg = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Build.0 = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Deploy.0 = Debug|ARM @@ -68,6 +54,22 @@ Global {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.ActiveCfg = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Build.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Deploy.0 = Release|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32 {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.ActiveCfg = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.Build.0 = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM64.ActiveCfg = Debug|ARM64 @@ -100,6 +102,22 @@ Global {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.ActiveCfg = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Build.0 = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Deploy.0 = Release|Win32 + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM64.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x64.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x86.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM64.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM64.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x64.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x64.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x86.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Calculator/App.xaml b/src/Calculator/App.xaml index cec835fb..918034c0 100644 --- a/src/Calculator/App.xaml +++ b/src/Calculator/App.xaml @@ -19,7 +19,6 @@ #FF2B2B2B - @@ -55,7 +54,6 @@ #FFE0E0E0 - @@ -89,7 +87,6 @@ 2 - @@ -356,7 +353,8 @@ - - - - + - - - - - - - -