diff --git a/.clang-tidy b/.clang-tidy index 6e3af6a2f..65ded0451 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -12,12 +12,10 @@ Checks: ['bugprone-*', 'readability-*', 'zircon-*', # Remove the following: - '-bugprone-easily-swappable-parameters', - '-bugprone-suspicious-semicolon', '-cert-dcl37-c', '-cert-dcl50-cpp', '-cert-dcl51-cpp', - '-cppcoreguidelines-avoid-magic-numbers', + '-cppcoreguidelines-avoid-do-while', '-cppcoreguidelines-pro-bounds-array-to-pointer-decay', '-cppcoreguidelines-pro-type-vararg', '-modernize-use-trailing-return-type', diff --git a/.clangd b/.clangd index 41c6fa14d..ba40c50f4 100644 --- a/.clangd +++ b/.clangd @@ -28,7 +28,8 @@ Diagnostics: 'cert-dcl51-cpp', 'cppcoreguidelines-pro-type-vararg', 'readability-braces-around-statements', - 'readability-identifier-length' + 'readability-identifier-length', + 'cppcoreguidelines-avoid-do-while' ] --- diff --git a/.idea/runConfigurations/Run_Tests.xml b/.idea/runConfigurations/Run_Tests.xml new file mode 100644 index 000000000..00d0cd790 --- /dev/null +++ b/.idea/runConfigurations/Run_Tests.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d49f8fc60..4c71e3a0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,24 +111,11 @@ SET(COMPONENTS calc::root calc::subtract calc::trig - matrix::ref) + matrix::ref +) # NEW_COMPONENT: PATCH Do NOT remove the previous comment. SET(TARGETS ${COMPONENTS} util) -SET(TEST_TARGETS_TEMP util fraction number mat2d factors format ${COMPONENTS}) - -FOREACH(TEST_TARGET IN LISTS TEST_TARGETS_TEMP) - SET(TARGET_NAME "test") - STRING(REPLACE "::" ";" COMPONENT_PARTS ${TEST_TARGET}) - LIST(LENGTH COMPONENT_PARTS LEN) - IF(LEN EQUAL 2) - LIST(GET COMPONENT_PARTS 1 TEST_TARGET) - ENDIF() - - CAPITALIZE(${TEST_TARGET} FILE_NAME) - STRING(CONCAT TARGET_NAME ${TARGET_NAME} ${FILE_NAME}) - LIST(APPEND TEST_TARGETS ${TARGET_NAME}) -ENDFOREACH() ADD_SUBDIRECTORY(src/) ADD_SUBDIRECTORY(lib/) diff --git a/README.md b/README.md index 4596218a5..43c2a4f70 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,9 @@ [![CMake Build](https://github.com/ZCG-coder/Steppable/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/ZCG-coder/Steppable/actions/workflows/cmake-multi-platform.yml) [![Doxygen Deploy](https://github.com/ZCG-coder/Steppable/actions/workflows/doxygen-gh-pages.yml/badge.svg)](https://github.com/ZCG-coder/Steppable/actions/workflows/doxygen-gh-pages.yml) -This project aims to make a Computer Algebra System (CAS) from scratch, and without any external libraries. +This project aims to build a Computer Algebra System (CAS) from scratch, without any external libraries. + +All documentation, usage instructions, and developer guides are available in the [project Wiki](https://github.com/ZCG-coder/Steppable/wiki). + +--- +For contribution guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/include/colors.hpp b/include/colors.hpp index 53203c8f7..de51068b9 100644 --- a/include/colors.hpp +++ b/include/colors.hpp @@ -37,6 +37,7 @@ #pragma once +#include #include /** @@ -61,6 +62,8 @@ namespace steppable::__internals::utils { + using ColorFunc = std::function; + /** * @brief Check if the output stream is a terminal. * @@ -87,6 +90,14 @@ namespace steppable::__internals::utils */ namespace colors { + /** + * @brief Does nothing. + * + * @param[in] stream The output stream to modify. + * @return The modified output stream. + */ + std::ostream& keepOriginal(std::ostream& stream); + /** * @brief Set the text color to black. * diff --git a/include/conPlot/conPlot.hpp b/include/conPlot/conPlot.hpp new file mode 100644 index 000000000..0ac74f407 --- /dev/null +++ b/include/conPlot/conPlot.hpp @@ -0,0 +1,40 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once + +#include "conPlot/conPlotTypes.hpp" + +/** + * @namespace steppable::graphing + * @brief Graphing utilities for showing graphs in the console. + */ +namespace steppable::graphing +{ + void conPlot(const std::vector& f, + const GraphOptions& graphOptions, + const std::vector& linesOptions); + + void conPlotBar(const std::vector>& numbers, + const BarGraphOptions& graphOptions, + const std::vector& barsOptions); +} // namespace steppable::graphing diff --git a/include/conPlot/conPlotInternals.hpp b/include/conPlot/conPlotInternals.hpp new file mode 100644 index 000000000..b2e56013f --- /dev/null +++ b/include/conPlot/conPlotInternals.hpp @@ -0,0 +1,51 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once + +#include "conPlot/conPlotTypes.hpp" +#include "steppable/number.hpp" + +#include + +/** + * @namespace steppable::graphing::__internals + * @brief Graphing utilities for showing graphs in the console. + */ +namespace steppable::graphing::__internals +{ + void conPlotLine(const Number& xGridSize, + const Number& yGridSize, + const Number& yMax, + const GraphOptions* graphOptions, + const LineOptions* lineOptions, + prettyPrint::ConsoleOutput* canvas, + std::map& fnValues); + + void drawTicks(prettyPrint::ConsoleOutput* canvas, + const Number& xGridSize, + const Number& yGridSize, + const Number& yMax, + const GraphOptionsBase* graphOptions); + + void drawGrid(prettyPrint::ConsoleOutput* canvas, const GraphOptionsBase* graphOptions); +} // namespace steppable::graphing::__internals diff --git a/include/conPlot/conPlotInterpolation.hpp b/include/conPlot/conPlotInterpolation.hpp new file mode 100644 index 000000000..2ca2c0a47 --- /dev/null +++ b/include/conPlot/conPlotInterpolation.hpp @@ -0,0 +1,46 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once + +#include + +namespace steppable::graphing +{ + /** + * @brief Fill in integral values with linear interpolation. + * + * @param data An x-y corresponding table of values. Must have at least 2 items. + * @param xMin Minimum `x` value. + * @param xMax Maximum `x` value. + */ + void linearInterpolateFill(std::map* data, long long xMin, long long xMax); + + /** + * @brief Fill in integral values with cubic interpolation. + * + * @param data An x-y corresponding table of values. Must have at least 4 items. + * @param xMin Minimum `x` value. + * @param xMax Maximum `x` value. + */ + void cubicInterpolateFill(std::map* data, long long xMin, long long xMax); +} // namespace steppable::graphing diff --git a/include/conPlot/conPlotTypes.hpp b/include/conPlot/conPlotTypes.hpp new file mode 100644 index 000000000..5d94ddb7a --- /dev/null +++ b/include/conPlot/conPlotTypes.hpp @@ -0,0 +1,224 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once + +#include "colors.hpp" +#include "steppable/number.hpp" +#include "steppable/parameter.hpp" + +#include +#include +#include + +namespace steppable::graphing +{ + using namespace steppable::__internals::utils; + using namespace steppable::__internals::symbols; + using namespace steppable::__internals::parameter; + + using GraphFn = std::function; + + /** + * @namespace steppable::graphing::GraphDot + * @brief Contains string values of dots to be graphed on the screen. + */ + namespace GraphDot + { + constexpr std::string_view ROUND_DOT = "\u25CF"; ///< Round dot + constexpr std::string_view BLOCK = "\u2588"; ///< Solid-filled block + constexpr std::string_view LIGHT_BLOCK_1 = "\u2591"; ///< Lightly-filled block + constexpr std::string_view LIGHT_BLOCK_2 = "\u2592"; ///< Medium-lightly-filled block + constexpr std::string_view LIGHT_BLOCK_3 = "\u2593"; ///< More densly-filled block + } // namespace GraphDot + + struct GraphOptionsBase + { + std::string title = "Graph"; ///< Title of the graph. + std::string xAxisTitle = "X Axis Title"; ///< Title of the `x` axis. + std::string yAxisTitle = "Y Axis Title"; ///< Title of the `y` axis. + + long long width = 30; ///< Width of the graph. + long long height = 20; ///< Height of the graph. + + long long xTickSpacing = 10; ///< Spacing between each `x` axis tick. + long long yTickSpacing = 5; ///< Spacing between each `y` axis tick. + + Number xMin = -1; ///< Minimum `x` value. + Number xMax = 1; ///< Maximum `x` value. + }; + + enum class ShadeOptions : std::uint8_t + { + NO_SHADE = 0, + SHADE_ABOVE_FIRST = 1, + SHADE_BELOW_SECOND = 2, + SHADE_BETWEEN_BOTH = 3, + SHADE_OUTSIDE_BOTH = 4, + SHADE_ABOVE_SECOND = 5, + SHADE_BELOW_FIRST = 6, + }; + + using NumberPair = std::pair; + + /** + * @struct steppable::graphing::GraphOptions + * @brief Stores the opotions for each graph shown on screen. + */ + struct GraphOptions : GraphOptionsBase + { + /** + * @brief Initializes a new `GraphOptions` instance. + * + * @param params The parameters to be passed to initialize the instance. + */ + template + GraphOptions(Params... params) + { + auto map = processParams(params...); + + PARAM_GET_FALLBACK(map, Number, xMin, -1); + PARAM_GET_FALLBACK(map, Number, xMax, 1); + PARAM_GET_FALLBACK(map, long long, width, 30LL); + PARAM_GET_FALLBACK(map, long long, height, 20LL); + PARAM_GET_FALLBACK(map, long long, xTickSpacing, 10LL); + PARAM_GET_FALLBACK(map, long long, yTickSpacing, 5LL); + PARAM_GET_FALLBACK(map, std::string, title, "Graph"); + PARAM_GET_FALLBACK(map, std::string, xAxisTitle, "X Axis Title"); + PARAM_GET_FALLBACK(map, std::string, yAxisTitle, "Y Axis Title"); + + this->xMin = xMin; + this->xMax = xMax; + this->width = width; + this->height = height; + this->xTickSpacing = xTickSpacing; + this->yTickSpacing = yTickSpacing; + this->title = title; + this->xAxisTitle = xAxisTitle; + this->yAxisTitle = yAxisTitle; + } + }; + + struct BarGraphOptions : GraphOptionsBase + { + long long yTickSpacing = 5; ///< Spacing between each `y` axis tick. + long long xTickSpacing = 2; ///< Width of a single bar + Number xMin = 0; + Number xMax = 100; + + /** + * @brief Initializes a new `GraphOptions` instance. + * + * @param params The parameters to be passed to initialize the instance. + */ + template + BarGraphOptions(Params... params) + { + auto map = processParams(params...); + + PARAM_GET_FALLBACK(map, long long, width, 30LL); + PARAM_GET_FALLBACK(map, long long, height, 20LL); + PARAM_GET_FALLBACK(map, long long, yTickSpacing, 5LL); + PARAM_GET_FALLBACK(map, long long, barWidth, 2LL); + PARAM_GET_FALLBACK(map, std::string, title, "Bar Graph"); + PARAM_GET_FALLBACK(map, std::string, xAxisTitle, "X Axis Title"); + PARAM_GET_FALLBACK(map, std::string, yAxisTitle, "Y Axis Title"); + + this->width = width; + this->height = height; + this->yTickSpacing = yTickSpacing; + this->title = title; + this->xAxisTitle = xAxisTitle; + this->yAxisTitle = yAxisTitle; + this->xTickSpacing = barWidth; + } + }; + + struct LineOptionsBase + { + std::string_view lineDot = GraphDot::BLOCK; ///< Dot type to be drawn on screen. + ColorFunc lineColor = colors::green; ///< Color of the dot to output. + std::string title = "Line"; ///< Name of the line to be shown in the legend. + }; + + template + concept LineOptionsDerivation = std::derived_from; + + /** + * @struct steppable::graphing::LineOptions + * @brief Stores options for each line graphed on screen. + */ + struct LineOptions : LineOptionsBase + { + long long samplesSpacing = 2; ///< Frequency to take a sample, units: grids. + NumberPair shadeValues = { 0, 0 }; + ShadeOptions shadeOptions = ShadeOptions::NO_SHADE; + std::string_view shadeDot = GraphDot::LIGHT_BLOCK_1; + + /** + * @brief Initializes a new `LineOptions` instance. + * + * @param params The parameters to be passed to initialize the instance. + */ + template + LineOptions(Params... params) + { + auto map = processParams(params...); + PARAM_GET_FALLBACK(map, std::string_view, lineDot, GraphDot::BLOCK); + PARAM_GET_FALLBACK(map, ColorFunc, lineColor, (ColorFunc)colors::green); + PARAM_GET_FALLBACK(map, std::string, title, "Line"s); + PARAM_GET_FALLBACK(map, long long, samplesSpacing, 2LL); + + PARAM_GET_FALLBACK(map, NumberPair, shadeValues, {}); + PARAM_GET_FALLBACK(map, ShadeOptions, shadeOptions, ShadeOptions::NO_SHADE); + PARAM_GET_FALLBACK(map, std::string_view, shadeBlock, GraphDot::LIGHT_BLOCK_1); + + this->lineDot = lineDot; + this->lineColor = lineColor; + this->title = title; + this->samplesSpacing = samplesSpacing; + + this->shadeValues = shadeValues; + this->shadeOptions = shadeOptions; + this->shadeDot = shadeBlock; + } + }; + + struct BarOptions : LineOptionsBase + { + std::string title = "Bar"; + + template + BarOptions(Params... params) + { + auto map = processParams(params...); + PARAM_GET_FALLBACK(map, std::string_view, block, GraphDot::BLOCK); + PARAM_GET_FALLBACK(map, ColorFunc, color, colors::keepOriginal); + PARAM_GET_FALLBACK(map, long long, barWidth, 2); + PARAM_GET_FALLBACK(map, std::string, title, "Bar"s); + + this->lineDot = block; + this->lineColor = color; + this->title = title; + } + }; +} // namespace steppable::graphing diff --git a/include/debugging.hpp b/include/debugging.hpp new file mode 100644 index 000000000..c7f7d1611 --- /dev/null +++ b/include/debugging.hpp @@ -0,0 +1,48 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once +#include +#include + +namespace steppable::inspect +{ + template + void printVector(const std::vector& vector) + { +#ifndef DEBUG + return; +#endif + + std::cout << "["; + for (size_t i = 0; i < vector.size(); i++) + { + std::cout << vector[i]; + + if (i != vector.size() - 1) + std::cout << ", "; + if (i % 5 == 0) + std::cout << "\n"; + } + std::cout << "]\n"; + } +} // namespace steppable::inspect \ No newline at end of file diff --git a/include/rounding.hpp b/include/rounding.hpp index 21c7bcfde..0f9864ce7 100644 --- a/include/rounding.hpp +++ b/include/rounding.hpp @@ -21,7 +21,9 @@ **************************************************************************************************/ #pragma once -#include + +#include "types/rounding.hpp" + #include namespace steppable::__internals::numUtils diff --git a/include/steppable/number.hpp b/include/steppable/number.hpp index f2756e251..4f7f97c59 100644 --- a/include/steppable/number.hpp +++ b/include/steppable/number.hpp @@ -30,10 +30,12 @@ #pragma once +#include "rounding.hpp" #include "testing.hpp" +#include "types/rounding.hpp" #include "util.hpp" -#include +#include #include #include @@ -43,35 +45,6 @@ */ namespace steppable { - /** - * @enum RoundingMode - * @brief Specifies how Steppable should round the number in operations. - */ - enum class RoundingMode : std::uint8_t - { - /// @brief Use the higher precision whenever possible. - USE_MAXIMUM_PREC = 0xFF, - - /// @brief Use the lower precision whenever possible. - USE_MINIMUM_PREC = 0x01, - - /// @brief Use the current precision. - USE_CURRENT_PREC = 0x02, - - /// @brief Use the other number's precision. - USE_OTHER_PREC = 0x03, - - /// @brief Do not append any decimal places. - DISCARD_ALL_DECIMALS = 0x00 - }; - - enum class Rounding : std::uint8_t - { - ROUND_DOWN = 0x00, ///< Rounds the number down. - ROUND_UP = 0x01, ///< Rounds the number up. - ROUND_OFF = 0x02, ///< Rounds the number off. - }; - /** * @class Number * @brief Represents a number with arbitrary precision. It basically stores the value as a string. @@ -111,11 +84,6 @@ namespace steppable } public: - /// @brief The default constructor. Initializes the number with a value of 0. - Number(); - - Number(const Number& rhs); - /** * @brief Initializes a number with a specified value. * @note By default, the value is 0. @@ -138,6 +106,7 @@ namespace steppable { this->mode = mode; prec = newPrec; + value = __internals::numUtils::roundOff(value, prec); } /** @@ -306,6 +275,17 @@ namespace steppable * @return The number as a string. */ [[nodiscard]] std::string present() const; + + /** + * @brief Gets the absolute value of the number. + * @return The absolute value of the current number. + */ + [[nodiscard]] Number abs() const + { + if (*this > 0) + return *this; + return -(*this); + } }; /** @@ -319,3 +299,11 @@ namespace steppable inline Number operator""_n(unsigned long long value) { return Number(value); } } // namespace literals } // namespace steppable + +template<> +struct std::hash +{ + size_t operator()(const steppable::Number& n) const { return hash()(n.present()); } +}; + +std::ostream& operator<<(std::ostream& os, const steppable::Number& number); diff --git a/include/steppable/parameter.hpp b/include/steppable/parameter.hpp new file mode 100644 index 000000000..b38192c9d --- /dev/null +++ b/include/steppable/parameter.hpp @@ -0,0 +1,195 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once + +#include "output.hpp" +#include "platform.hpp" + +#include +#include +#include +#include +#include + +using namespace std::literals; + +/** + * @namespace steppable::__internals::parameter + * @brief Contains the parameter utilities to allow named parameters to be passed into functions. + */ +namespace steppable::__internals::parameter +{ + /** + * @struct ValuedParameter + * @brief A parameter with a name and value. + */ + struct ValuedParameter + { + std::string name; ///< The name of the parameter. + std::any value; ///< The value of the parameter. + }; + + /// @brief A type containing all the parameters of a function, in calling order. + using ValueMap = std::vector; + + /** + * @struct Parameter + * @brief Stores the name of a parameter + */ + struct Parameter // NOLINT(cppcoreguidelines-special-member-functions) + { + std::string name; ///< The name of the parameter. + + /** + * @brief Shorthand function to facilitate setting values of parameters. + * + * @param value The value to set for the parameter. + * @return A `ValuedParameter` object representing the current parameter. + */ + template + ValuedParameter operator=(T value) // NOLINT(cppcoreguidelines-c-copy-assignment-signature) + { + return { .name = name, .value = std::move(value) }; + } + + Parameter& operator=(const Parameter&) = delete; + + /** + * @brief Initializes a new `Parameter` object. + * + * @param name The name of the parameter. + */ + explicit Parameter(std::string name) : name(std::move(name)) {} + }; + + /** + * @struct ParameterMap + * @brief An object containing all parameters and their values passed into a function. + */ + struct ParameterMap + { + ValueMap values; ///< All `ValuedParameter` objects passed to a function. + + /** + * @brief Initializes a new `ParameterMap` object. + * + * @param values All `ValuedParameter` objects passed to a function. + */ + ParameterMap(ValueMap values) : values(std::move(values)) {} + + /** + * @brief Checks the presence of all the arguments, in the order which they should be specified. Prints an error + * message and exits the program if one of the parameter names is not specified according to the order, or the + * size of the name vector is not the same as the number of parameters passed into the function. + * + * @param names The names to check. + */ + void checkParameterOrder(const std::vector& names); + + /** + * @brief Checks the presence of all the arguments, no matter which order they are specified. Prints an error + * message and exits the program if one of the parameter names specified is not found, or the size of the name + * vector is not the same as the number of parameters passed into the function. + * + * @param names The names to check. + */ + void checkParameterNameUnordered(const std::vector& names); + + /** + * @brief Gets an item from the current parameter map. + * + * @param name The name of the parameter. + * @return A value from the parameter list. + */ + template + ValueT getItem(const std::string& name) + { + ValuedParameter value; + for (const auto& obj : values) + if (obj.name == name) + value = obj; + if (value.name == "") + { + output::error("ParameterMap::getItem"s, "Name {0} not found."s, { name }); + utils::programSafeExit(1); + } + return std::any_cast(value.value); + } + + /** + * @brief Gets an item from the current parameter map. Returns the fallback the matching parameter if not found. + * + * @param name The name of the parameter. + * @param fallback If no matching parameter is specified, returns the fallback. + * @return A value from the parameter list or the fallback. + */ + template + ValueT getItem(const std::string& name, const ValueT& fallback) + { + ValuedParameter value; + for (const auto& obj : values) + if (obj.name == name) + value = obj; + if (value.name == "") + return fallback; + return std::any_cast(value.value); + } + }; + + /** + * @brief Convert a literal to a parameter, using a shorthand notation. + * + * @param name The name of the parameter, expressed as a simple string literal. + * @return A `Parameter` object to set the value of the parameter. + */ + inline Parameter operator""_p(const char* name, size_t /*unused*/) { return Parameter(name); } + + /** + * @brief Process parameters passed into a function. + * @details Takes in all arguments from a variadic function, and processes them as parameters. + * + * @param params The parameters passed into a function. + * @return An instance of `ParameterMap` containing a list of the parameters. + */ + template + ParameterMap processParams(Params... params) + { + return { std::vector{ params... } }; + } +} // namespace steppable::__internals::parameter + +/// @brief Get a parameter from a parameter list. +#define PARAM_GET(map, type, name) \ + type name; \ + do \ + { \ + (name) = (map).template getItem(#name); \ + } while (0) + +/// @brief Get a parameter from a parameter list. If not found, return the fallback. +#define PARAM_GET_FALLBACK(map, type, name, fallback) \ + type name; \ + do \ + { \ + (name) = (map).template getItem(#name, fallback); \ + } while (0) diff --git a/include/symbols.hpp b/include/symbols.hpp index bff6d8b73..fd3a27a58 100644 --- a/include/symbols.hpp +++ b/include/symbols.hpp @@ -32,10 +32,14 @@ #pragma once +#include "colors.hpp" + #include #include +#include #include #include +#include #include /** @@ -44,6 +48,8 @@ */ namespace steppable::prettyPrint { + using namespace __internals::utils; + /** * @brief Represents a position in the console. */ @@ -66,6 +72,13 @@ namespace steppable::prettyPrint // PrintingAlignment alignment; // }; + enum class HorizontalAlignment : std::uint8_t + { + LEFT = 0, + CENTER = 1, + RIGHT = 2 + }; + /** * @brief Represents a console output buffer. */ @@ -76,7 +89,7 @@ namespace steppable::prettyPrint Position curPos; /// @brief The buffer object. - std::vector> buffer; + std::vector> buffer; /// @brief The height of the buffer. size_t height = 10; @@ -84,6 +97,20 @@ namespace steppable::prettyPrint /// @brief The width of the buffer. size_t width = 10; + /** + * @brief Writes a string to the buffer. + * + * @param s The string to write. + * @param pos The position to write to. + * @param updatePos Whether to update the current position. + * @param color Color of the text to write. + */ + void _write(const std::string& s, + const Position& pos, + bool updatePos = false, + const ColorFunc& color = colors::keepOriginal, + const HorizontalAlignment& alignment = HorizontalAlignment::LEFT); + public: /** * @brief Creates a new console output buffer. @@ -100,8 +127,14 @@ namespace steppable::prettyPrint * @param dLine The change in line. * @param dCol The change in column. * @param updatePos Whether to update the current position. + * @param color Color of the text to write. */ - void write(char c, long long dLine, long long dCol, bool updatePos = false); + void write(char c, + long long dLine, + long long dCol, + bool updatePos = false, + const ColorFunc& color = colors::keepOriginal, + const HorizontalAlignment& alignment = HorizontalAlignment::LEFT); /** * @brief Writes a character to the buffer. @@ -109,17 +142,30 @@ namespace steppable::prettyPrint * @param c The character to write. * @param pos The position to write to. * @param updatePos Whether to update the current position. + * @param color Color of the text to write. */ - void write(char c, const Position& pos, bool updatePos = false); + void write(char c, + const Position& pos, + bool updatePos = false, + const ColorFunc& color = colors::keepOriginal, + const HorizontalAlignment& alignment = HorizontalAlignment::LEFT); /** - * @brief Writes a string to the buffer. + * @brief Writes a `string_view` object to the buffer. * * @param s The string to write. * @param pos The position to write to. * @param updatePos Whether to update the current position. + * @param color Color of the text to write. */ - void write(const std::string& s, const Position& pos, bool updatePos = false); + void write(const std::string_view& s, + const Position& pos, + bool updatePos = false, + const ColorFunc& color = colors::keepOriginal, + const HorizontalAlignment& alignment = HorizontalAlignment::LEFT) + { + _write(static_cast(s), pos, updatePos, color, alignment); + } /** * @brief Gets the buffer as a string. @@ -145,6 +191,112 @@ namespace steppable::prettyPrint size_t getStringHeight(const std::string& s); } // namespace steppable::prettyPrint +namespace steppable::__internals::stringUtils +{ + + /** + * @brief Checks if a Unicode code point represents a zero-width character. + * + * Zero-width characters do not consume display space (e.g., combining marks, zero-width spaces). + * + * @param codepoint The Unicode code point to check. + * @return true if the code point is a zero-width character, false otherwise. + */ + bool isZeroWidthCharacter(uint32_t codepoint); + + /** + * @brief Decodes a UTF-8 encoded string into a Unicode code point. + * + * This function reads one UTF-8 encoded character from the string, starting at index `i`, + * and advances the index to the next character. + * + * @param str The UTF-8 encoded string. + * @param i The current position in the string (updated after decoding). + * @return The Unicode code point of the character at the given position. + */ + uint32_t getCodepoint(const std::string& str, size_t& i); + + /** + * @brief Calculates the display width of a UTF-8 encoded string. + * + * This function computes the total display width of a string, correctly handling wide, narrow, + * zero-width, and CJK characters. CJK characters are counted as one character. + * + * @param utf8Str The UTF-8 encoded string. + * @return The total display width of the string. + */ + size_t getUnicodeDisplayWidth(const std::string& utf8Str); + + bool utf8Decode(const std::string& s, size_t& i, uint32_t& cp); + + bool isEmojiBase(uint32_t cp); + + bool isClusterExtender(uint32_t cp); + + class GraphemeIterator + { + public: + GraphemeIterator(std::string s) : str(std::move(s)) {} + + // Returns false when there are no more clusters + bool next(std::string& cluster) + { + if (pos >= str.size()) + return false; + size_t start = pos; + uint32_t cp = 0; + size_t temp = pos; + if (!utf8Decode(str, temp, cp)) + return false; + + // For emoji: group sequences with ZWJ or skin tone + if (isEmojiBase(cp) || (cp >= 0x1F1E6 && cp <= 0x1F1FF)) + { + pos = temp; + while (true) + { + size_t save = pos; + uint32_t nextcp = 0; + if (!utf8Decode(str, save, nextcp)) + break; + if (isClusterExtender(nextcp) || + // For flags: two regional indicators + ((cp >= 0x1F1E6 && cp <= 0x1F1FF) && (nextcp >= 0x1F1E6 && nextcp <= 0x1F1FF))) + { + pos = save; + } + else + { + break; + } + } + cluster = str.substr(start, pos - start); + return true; + } + + // For combining marks: group base + following combining + pos = temp; + while (true) + { + size_t save = pos; + uint32_t nextcp = 0; + if (!utf8Decode(str, save, nextcp)) + break; + if (isZeroWidthCharacter(nextcp)) + pos = save; + else + break; + } + cluster = str.substr(start, pos - start); + return true; + } + + private: + std::string str; + size_t pos{}; + }; +} // namespace steppable::__internals::stringUtils + /** * @namespace steppable::__internals::symbols * @brief The namespace containing various unicode symbols. @@ -249,6 +401,33 @@ namespace steppable::__internals::symbols * @return The surd expression. */ std::string makeSurd(const std::string& radicand); + + /** + * @namespace steppable::__internals::symbols::BoxDrawing + * @brief Defines easy ascess to Unicode box-drawing characters. + */ + namespace BoxDrawing + { + constexpr std::string_view DOTTED_VERTICAL = "\u2575"; ///< Dashed vertical line + constexpr std::string_view DOTTED_HORIZONTAL = "\u2574"; ///< Dashed horizontal line + + constexpr std::string_view HORIZONTAL = "\u2500"; ///< Solid horizontal line + constexpr std::string_view VERTICAL = "\u2502"; ///< Solid vertical line + + // region CONNECTORS + /// @brief Horizontal line that connects to a vertical one, i.e., in `_|_` shape. + constexpr std::string_view HORIZONTAL_UP = "\u2534"; + + /// @brief Vertical line that connects to a left one, i.e., in `-|` shape. + constexpr std::string_view VERTICAL_LEFT = "\u2524"; + // endregion + + // region CORNERS + constexpr std::string_view BOTTOM_RIGHT_CORNER = "\u2518"; ///< The bottom right corner + // endregion + + constexpr std::string_view CROSS = "\u253C"; ///< A combining cross between a horizontal and vertical line. + } // namespace BoxDrawing } // namespace steppable::__internals::symbols /** diff --git a/include/types/concepts.hpp b/include/types/concepts.hpp index 058c183fe..b2458cf29 100644 --- a/include/types/concepts.hpp +++ b/include/types/concepts.hpp @@ -26,7 +26,10 @@ * @date 23 Jun 2025 */ +#pragma once + #include +#include #include /** @@ -43,4 +46,10 @@ namespace steppable::concepts concept Presentable = requires(ObjectT object) { { object.present() } -> std::same_as; }; + + template + /// @brief Represents any streams with a `.operator<<()` method. + concept Stream = requires(ObjectT object) { + { object.operator<<() } -> std::same_as; + }; } // namespace steppable::concepts diff --git a/include/types/rounding.hpp b/include/types/rounding.hpp new file mode 100644 index 000000000..cf1478872 --- /dev/null +++ b/include/types/rounding.hpp @@ -0,0 +1,57 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#pragma once + +#include + +namespace steppable +{ + /** + * @enum RoundingMode + * @brief Specifies how Steppable should round the number in operations. + */ + enum class RoundingMode : std::uint8_t + { + /// @brief Use the higher precision whenever possible. + USE_MAXIMUM_PREC = 0xFF, + + /// @brief Use the lower precision whenever possible. + USE_MINIMUM_PREC = 0x01, + + /// @brief Use the current precision. + USE_CURRENT_PREC = 0x02, + + /// @brief Use the other number's precision. + USE_OTHER_PREC = 0x03, + + /// @brief Do not append any decimal places. + DISCARD_ALL_DECIMALS = 0x00 + }; + + enum class Rounding : std::uint8_t + { + ROUND_DOWN = 0x00, ///< Rounds the number down. + ROUND_UP = 0x01, ///< Rounds the number up. + ROUND_OFF = 0x02, ///< Rounds the number off. + }; +} // namespace steppable diff --git a/include/util.hpp b/include/util.hpp index b553f2da7..e071ea9cf 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -41,9 +41,11 @@ #include "colors.hpp" #include "output.hpp" #include "platform.hpp" +#include "types/concepts.hpp" #include #include +#include #include #include #include @@ -607,4 +609,17 @@ namespace steppable::__internals::stringUtils } return out.str(); } + + template + T toNumeric(const std::string& s) + { + auto value = T{}; + + if (auto result = std::from_chars(s.data(), s.data() + s.size(), value); result.ec != std::errc{}) + { + output::error("toNumeric"s, "Invalid argument"s); + utils::programSafeExit(1); + } + return value; + } } // namespace steppable::__internals::stringUtils diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2c731b357..a489817e9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -20,27 +20,31 @@ # SOFTWARE. # ##################################################################################################### -if(NOT ${STP_NO_BINDINGS}) # Only create the bindings if needed - set(Python_EXECUTABLE ${Python3_EXECUTABLE}) +IF(NOT ${STP_NO_BINDINGS}) # Only create the bindings if needed + SET(Python_EXECUTABLE ${Python3_EXECUTABLE}) # Detect the installed nanobind package and import it into CMake - execute_process( + EXECUTE_PROCESS( COMMAND "${Python3_EXECUTABLE}" -m nanobind --cmake_dir OUTPUT_STRIP_TRAILING_WHITESPACE - OUTPUT_VARIABLE NB_DIR) - message(TRACE "Found nanobind: ${NB_DIR}") - list(APPEND CMAKE_PREFIX_PATH "${NB_DIR}") - find_package(nanobind CONFIG REQUIRED) + OUTPUT_VARIABLE NB_DIR + ) + MESSAGE(TRACE "Found nanobind: ${NB_DIR}") + LIST(APPEND CMAKE_PREFIX_PATH "${NB_DIR}") + FIND_PACKAGE(nanobind CONFIG REQUIRED) # Python bindings for Steppable. - set(Python_INTERPRETER_ID ${Python3_INTERPRETER_ID} CACHE INTERNAL "Compatibility for nanobind") - nanobind_add_module(steppyble STABLE_ABI NB_STATIC LTO bindings.cpp) + SET(Python_INTERPRETER_ID + ${Python3_INTERPRETER_ID} + CACHE INTERNAL "Compatibility for nanobind" + ) + NANOBIND_ADD_MODULE(steppyble STABLE_ABI NB_STATIC LTO bindings.cpp) - if(NOT ${WINDOWS}) - target_link_options(steppyble PRIVATE -Bsymbolic) - endif() - set_target_properties(steppyble PROPERTIES POSITION_INDEPENDENT_CODE ON) - target_link_libraries(steppyble PRIVATE func steppable) - target_compile_definitions(steppyble PRIVATE NO_MAIN STP_BINDINGS) - target_include_directories(steppyble PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include) -endif() + IF(NOT ${WINDOWS}) + TARGET_LINK_OPTIONS(steppyble PRIVATE -Bsymbolic) + ENDIF() + SET_TARGET_PROPERTIES(steppyble PROPERTIES POSITION_INDEPENDENT_CODE ON) + TARGET_LINK_LIBRARIES(steppyble PRIVATE func steppable) + TARGET_COMPILE_DEFINITIONS(steppyble PRIVATE NO_MAIN STP_BINDINGS) + TARGET_INCLUDE_DIRECTORIES(steppyble PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include) +ENDIF() diff --git a/res/translations/en-US/mat2d.stp_localized b/res/translations/en-US/mat2d.stp_localized new file mode 100644 index 000000000..24530dba2 --- /dev/null +++ b/res/translations/en-US/mat2d.stp_localized @@ -0,0 +1,35 @@ +##################################################################################################### +# Copyright (c) 2023-2025 NWSOFT # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy # +# of this software and associated documentation files (the "Software"), to deal # +# in the Software without restriction, including without limitation the rights # +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # +# copies of the Software, and to permit persons to whom the Software is # +# furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in all # +# copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +##################################################################################################### + +# NOTE: This file is generated. Do not edit it manually. Any changes will be overwritten. +# STR_GUID: (key) / STRING TRANSLATED: (string) +# eg: a491b7b2-1239-4acb-9045-0747d806b96f >> "Hello World!" +# Recommended syntax highlighting: Bash Script +75953952-0eea-4716-a006-d0f2e7a8f6c9 >> "Matrix has non-uniform dimensions." +8d4e4757-415b-4aed-8f5e-26b3503a95dd >> "Incorrect x parameter. {0} exceeds the number of columns in this matrix ({1})." +e7cb3f0b-11d8-4e12-8c93-4a1021b15e10 >> "Incorrect y parameter. {0} exceeds the number of rows in this matrix ({1})." +fe78bdc2-b409-4078-8e0e-313c46977f25 >> "Matrix is not a square." +88331f88-3a4c-4b7e-9b43-b51a1d1020e2 >> "Matrix dimensions mismatch. Expect {0} columns. Got {1} columns." +34e92306-a4d8-4ff0-8441-bfcd29771e94 >> "Matrix dimensions mismatch. Expect {0} rows. Got {1} rows." +17b6aadd-bce1-4558-a7cc-7a099f00e57c >> "Incorrect matrix dimensions for multiplication." +8966ce13-8ae9-4f14-ba4e-837b98a4c9fa >> "For matrix multiplication, the number of columns in the first matrix must be equal to the number of rows in the second matrix." +f255d307-9482-442b-a523-61a1c7465f9c >> "Incorrect RHS matrix dimensions. Expect {0} rows, got {1}." \ No newline at end of file diff --git a/res/translations/mat2d.stp_strings b/res/translations/mat2d.stp_strings new file mode 100644 index 000000000..5f6120909 --- /dev/null +++ b/res/translations/mat2d.stp_strings @@ -0,0 +1,35 @@ +##################################################################################################### +# Copyright (c) 2023-2025 NWSOFT # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy # +# of this software and associated documentation files (the "Software"), to deal # +# in the Software without restriction, including without limitation the rights # +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # +# copies of the Software, and to permit persons to whom the Software is # +# furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in all # +# copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +##################################################################################################### + +# NOTE: This file is generated. Do not edit it manually. Any changes will be overwritten. +# STR_GUID: (key) / STRING ORIGINAL: (string) +# eg: a491b7b2-1239-4acb-9045-0747d806b96f >> "Hello World!" +# Recommended syntax highlighting: Bash Script +75953952-0eea-4716-a006-d0f2e7a8f6c9 >> "Matrix has non-uniform dimensions." +8d4e4757-415b-4aed-8f5e-26b3503a95dd >> "Incorrect x parameter. {0} exceeds the number of columns in this matrix ({1})." +e7cb3f0b-11d8-4e12-8c93-4a1021b15e10 >> "Incorrect y parameter. {0} exceeds the number of rows in this matrix ({1})." +fe78bdc2-b409-4078-8e0e-313c46977f25 >> "Matrix is not a square." +88331f88-3a4c-4b7e-9b43-b51a1d1020e2 >> "Matrix dimensions mismatch. Expect {0} columns. Got {1} columns." +34e92306-a4d8-4ff0-8441-bfcd29771e94 >> "Matrix dimensions mismatch. Expect {0} rows. Got {1} rows." +17b6aadd-bce1-4558-a7cc-7a099f00e57c >> "Incorrect matrix dimensions for multiplication." +8966ce13-8ae9-4f14-ba4e-837b98a4c9fa >> "For matrix multiplication, the number of columns in the first matrix must be equal to the number of rows in the second matrix." +f255d307-9482-442b-a523-61a1c7465f9c >> "Incorrect RHS matrix dimensions. Expect {0} rows, got {1}." diff --git a/res/translations/zh-HK/mat2d.stp_localized b/res/translations/zh-HK/mat2d.stp_localized new file mode 100644 index 000000000..7db1b9c58 --- /dev/null +++ b/res/translations/zh-HK/mat2d.stp_localized @@ -0,0 +1,35 @@ +##################################################################################################### +# Copyright (c) 2023-2025 NWSOFT # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy # +# of this software and associated documentation files (the "Software"), to deal # +# in the Software without restriction, including without limitation the rights # +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # +# copies of the Software, and to permit persons to whom the Software is # +# furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in all # +# copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +##################################################################################################### + +# NOTE: This file is generated. Do not edit it manually. Any changes will be overwritten. +# STR_GUID: (key) / STRING TRANSLATED: (string) +# eg: a491b7b2-1239-4acb-9045-0747d806b96f >> "Hello World!" +# Recommended syntax highlighting: Bash Script +75953952-0eea-4716-a006-d0f2e7a8f6c9 >> "矩陣大小不均勻。" +8d4e4757-415b-4aed-8f5e-26b3503a95dd >> "x 參數錯誤。{0} 超過了矩陣中的列數({1})。" +e7cb3f0b-11d8-4e12-8c93-4a1021b15e10 >> "y 參數錯誤。{0} 超過了矩陣中的行數({1})。" +fe78bdc2-b409-4078-8e0e-313c46977f25 >> "矩陣不為方形。" +88331f88-3a4c-4b7e-9b43-b51a1d1020e2 >> "矩陣大小不匹配。需要 {0} 列,輸入為 {1} 列。" +34e92306-a4d8-4ff0-8441-bfcd29771e94 >> "矩陣大小不匹配。需要 {0} 行,輸入為 {1} 行。" +17b6aadd-bce1-4558-a7cc-7a099f00e57c >> "矩陣大小不適用乘法。" +8966ce13-8ae9-4f14-ba4e-837b98a4c9fa >> "兩個矩陣的乘法僅當第一個矩陣的列數和B的行數相等時才能定義。" +f255d307-9482-442b-a523-61a1c7465f9c >> "矩陣 B 大小錯誤。需要 {0} 行,輸入為 {1} 行。" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 112145fc5..dff57541d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,8 @@ # SOFTWARE. # ##################################################################################################### +ADD_SUBDIRECTORY(conPlot) + ADD_LIBRARY( util STATIC argParse.cpp @@ -62,12 +64,16 @@ FOREACH(COMPONENT IN LISTS COMPONENTS) ENDFOREACH() # FUNC: Library containing all Steppable functions. -ADD_LIBRARY(func STATIC ${CALCULATOR_FILES} - steppable/fraction.cpp - steppable/mat2d.cpp - steppable/number.cpp - rounding.cpp - factors.cpp) +ADD_LIBRARY( + func STATIC + ${CALCULATOR_FILES} + ${CONPLOT_FILES} + steppable/fraction.cpp + steppable/mat2d.cpp + steppable/number.cpp + rounding.cpp + factors.cpp +) SET_TARGET_PROPERTIES(func PROPERTIES POSITION_INDEPENDENT_CODE ON) TARGET_INCLUDE_DIRECTORIES(steppable PRIVATE ${STP_BASE_DIRECTORY}/include/) @@ -75,6 +81,8 @@ TARGET_INCLUDE_DIRECTORIES(func PRIVATE ${STP_BASE_DIRECTORY}/include/) TARGET_INCLUDE_DIRECTORIES(util PRIVATE ${STP_BASE_DIRECTORY}/include/) TARGET_LINK_LIBRARIES(steppable PRIVATE util) -TARGET_LINK_LIBRARIES(func PRIVATE steppable) +TARGET_LINK_LIBRARIES(steppable PRIVATE conPlot) +TARGET_LINK_LIBRARIES(func PRIVATE util) +TARGET_LINK_LIBRARIES(func PRIVATE conPlot) TARGET_COMPILE_DEFINITIONS(func PRIVATE NO_MAIN) TARGET_COMPILE_DEFINITIONS(steppable PRIVATE NO_MAIN) diff --git a/src/calc/add/add.cpp b/src/calc/add/add.cpp index 1b3a8a3fc..009f51990 100644 --- a/src/calc/add/add.cpp +++ b/src/calc/add/add.cpp @@ -92,14 +92,8 @@ namespace steppable::__internals::calc const bool aIsDecimal = not isZeroString(aDecimal); const bool bIsDecimal = not isZeroString(bDecimal); - if (negative) + if (negative or (aIsNegative and bIsNegative)) resultIsNegative = true; - else if (aIsNegative and bIsNegative) - { - resultIsNegative = true; - aIsNegative = false; - bIsNegative = false; - } else if (aIsNegative) { if (steps == 2) @@ -158,6 +152,11 @@ namespace steppable::__internals::calc if (sumDigits.front() == 0 and properlyFormat) sumDigits.erase(sumDigits.begin()); + if (aIsNegative) + aInteger = "-" + aInteger; + if (bIsNegative) + bInteger = "-" + bInteger; + return reportAdd( aInteger, aDecimal, bInteger, bDecimal, sumDigits, carries, resultIsNegative, steps, properlyFormat); } diff --git a/src/calc/add/addReport.cpp b/src/calc/add/addReport.cpp index bfd0f5ce1..37d8a2d98 100644 --- a/src/calc/add/addReport.cpp +++ b/src/calc/add/addReport.cpp @@ -74,13 +74,15 @@ std::string reportAdd(const std::string& aInteger, if (bOut.length() > aOut.length()) ss << std::string(bOut.length() - aOut.length(), ' '); for (const char aChar : aOut) - ss << aChar << " "; + if (aChar != '-') + ss << aChar << " "; ss << '\n' << "+ "; // Print an add sign before printing b if (aOut.length() > bOut.length()) ss << std::string(aOut.length() - bOut.length(), ' '); for (const char bChar : bOut) - ss << bChar << " "; + if (bChar != '-') + ss << bChar << " "; ss << '\n' << " "; for (const int c : carries) @@ -106,6 +108,8 @@ std::string reportAdd(const std::string& aInteger, ss << aOut << " + " << bOut << " = "; std::string outStr; + if (resultIsNegative) + outStr = "-"; for (const auto c : sumDigits) if (c == -1) outStr += "."; diff --git a/src/calc/division/division.cpp b/src/calc/division/division.cpp index 1df54e640..2eba42298 100644 --- a/src/calc/division/division.cpp +++ b/src/calc/division/division.cpp @@ -130,6 +130,10 @@ namespace steppable::__internals::calc error("division", $("division", "977f3c9f-01c3-49e4-bf4a-94d7c58bbe82", { _number })); return "Infinity"; } + if (isZeroString(_number)) + { + return "0"; + } if (compare(_number, _divisor, 0) == "2") { diff --git a/src/calc/subtract/subtract.cpp b/src/calc/subtract/subtract.cpp index c073a6707..4bd074e9f 100644 --- a/src/calc/subtract/subtract.cpp +++ b/src/calc/subtract/subtract.cpp @@ -76,7 +76,7 @@ namespace steppable::__internals::calc { if (steps == 2) // Adding {0} and {1} since {0} is negative - std::cout << $("subtract", "063f0bd2-a4ca-4433-97c0-8baa73cd0e7c", { a.substr(1), b }) << "\n"; + std::cout << $("subtract", "063f0bd2-a4ca-4433-97c0-8baa73cd0e7c", { a.substr(1), b, b }) << "\n"; auto addResult = add(a.substr(1), b, steps, true); auto res = addResult.substr(addResult.find_last_of(' ') + 1); if (steps == 2) @@ -84,23 +84,22 @@ namespace steppable::__internals::calc // Replace last line with a subtraction report std::stringstream ss; ss << addResult.substr(0, addResult.find_last_of('\n')) << '\n'; - ss << THEREFORE << ' ' << a << " - " << b << " = -" << res; + ss << THEREFORE << ' ' << a << " - " << b << " = " << res; return ss.str(); } if (steps == 1) { std::stringstream ss; - ss << THEREFORE << ' ' << a << " - " << b << " = -" << res; + ss << THEREFORE << ' ' << a << " - " << b << " = " << res; return ss.str(); } - return "-" + addResult; + return standardizeNumber(addResult); } if (bIsNegative) { if (steps == 2) // Adding {0} and {1} since {2} is negative std::cout << $("subtract", "063f0bd2-a4ca-4433-97c0-8baa73cd0e7c", { a, b.substr(1), b }) << "\n"; - resultIsNegative = false; return add(a, b.substr(1), steps); } if (compare(a, b, 0) == "0") @@ -122,7 +121,7 @@ namespace steppable::__internals::calc << subtractResult.substr(subtractResult.find_last_of(' ') + 1); return ss.str(); } - return "-" + subtractResult; + return standardizeNumber("-" + subtractResult); } std::string aStr = aInteger + aDecimal; diff --git a/src/colors.cpp b/src/colors.cpp index d7b777cde..84f281dca 100644 --- a/src/colors.cpp +++ b/src/colors.cpp @@ -25,14 +25,46 @@ #include #ifdef WINDOWS -#include -#include -#include -#include -// ReSharper disable once CppInconsistentNaming -#define isatty _isatty -// ReSharper disable once CppInconsistentNaming -#define fileno _fileno + // clang-format off + #include + // clang-format on + + #include + #include + #include + // ReSharper disable once CppInconsistentNaming + #define isatty _isatty + // ReSharper disable once CppInconsistentNaming + #define fileno _fileno + +double* sysVersion = nullptr; + +// https://stackoverflow.com/a/52122386/14868780 +/** +* @brief Gets the Windows version as a double. +* @warning This method is only available on Windows. +* @returns The Windows version as double, for example, 10.0. +*/ +double getSysOpType() +{ + if (sysVersion != nullptr) + return *sysVersion; + + double ret = 0; + sysVersion = new double; + NTSTATUS(WINAPI * RtlGetVersion)(LPOSVERSIONINFOEXW); + OSVERSIONINFOEXW osInfo; + + *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); + + if (NULL != RtlGetVersion) + { + osInfo.dwOSVersionInfoSize = sizeof(osInfo); + RtlGetVersion(&osInfo); + ret = (double)osInfo.dwMajorVersion; + } + *sysVersion = ret; +} #else #include #endif @@ -42,7 +74,7 @@ namespace steppable::__internals::utils bool isTerminal(const std::ostream& stream) { #ifdef WINDOWS - if (IsWindows10OrGreater()) + if (getSysOpType() >= 10.0) return isatty(fileno(stdout)) != 0; return false; // The Windows console in Windows 7 does not support ANSI escapes #else @@ -59,6 +91,12 @@ namespace steppable::__internals::utils namespace colors { + std::ostream& keepOriginal(std::ostream& stream) + { + stream << ""; + return stream; + } + std::ostream& black(std::ostream& stream) { if (isTerminal(stream)) diff --git a/src/conPlot/CMakeLists.txt b/src/conPlot/CMakeLists.txt new file mode 100644 index 000000000..c1aa9e841 --- /dev/null +++ b/src/conPlot/CMakeLists.txt @@ -0,0 +1,29 @@ +##################################################################################################### +# Copyright (c) 2023-2025 NWSOFT # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy # +# of this software and associated documentation files (the "Software"), to deal # +# in the Software without restriction, including without limitation the rights # +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # +# copies of the Software, and to permit persons to whom the Software is # +# furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in all # +# copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +##################################################################################################### + +SET(CONPLOT_FILES ${STP_BASE_DIRECTORY}/src/conPlot/conPlot.cpp ${STP_BASE_DIRECTORY}/src/conPlot/conPlotInternals.cpp + ${STP_BASE_DIRECTORY}/src/conPlot/conPlotInterpolation.cpp +) + +ADD_LIBRARY(conPlot STATIC ${CONPLOT_FILES}) +SET_TARGET_PROPERTIES(conPlot PROPERTIES POSITION_INDEPENDENT_CODE ON) +TARGET_INCLUDE_DIRECTORIES(conPlot PRIVATE ${STP_BASE_DIRECTORY}/include/) diff --git a/src/conPlot/conPlot.cpp b/src/conPlot/conPlot.cpp new file mode 100644 index 000000000..5e2795608 --- /dev/null +++ b/src/conPlot/conPlot.cpp @@ -0,0 +1,173 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#include "conPlot/conPlot.hpp" + +#include "conPlot/conPlotInternals.hpp" +#include "conPlot/conPlotTypes.hpp" +#include "debugging.hpp" +#include "steppable/number.hpp" +#include "symbols.hpp" + +#include +#include + +namespace steppable::graphing +{ + namespace + { + template + void conPlotLegend(const GraphOptionsBase* graphOptions, + const std::vector* lineOptions, + prettyPrint::ConsoleOutput* canvas) + { + long long yLoc = graphOptions->height + 7; + canvas->write("Legend", { .x = 0, .y = yLoc++ }, false, formats::bold); + for (size_t fnIdx = 0; fnIdx < lineOptions->size(); fnIdx++) + { + const auto& lineOption = lineOptions->at(fnIdx); + yLoc += static_cast(fnIdx); + canvas->write(lineOption.title, { .x = 0, .y = yLoc }, false); + + for (int i = 0; i < graphOptions->xTickSpacing; i++) + canvas->write(lineOption.lineDot, + { .x = static_cast(lineOption.title.length()) + 1 + i, .y = yLoc }, + false, + lineOption.lineColor); + } + } + } // namespace + + void conPlotBar(const std::vector>& numbers, + const BarGraphOptions& graphOptions, + const std::vector& barsOptions) + { + // Create buffer + prettyPrint::ConsoleOutput canvas(graphOptions.height + 10, + graphOptions.width + 12 + + steppable::__internals::stringUtils::getUnicodeDisplayWidth( + graphOptions.yAxisTitle)); // Extra space for labels + + Number maxValue = 0; + size_t datasetSize = numbers[0].size(); + for (const auto& dataset : numbers) + { + if (dataset.size() != datasetSize) + { + output::error("conPlotBar"s, "Bar plot datasets are not of the same size"s); + programSafeExit(1); + } + for (const auto& number : dataset) + maxValue = std::max(maxValue, number); + } + const auto& gridSize = maxValue / graphOptions.height; + + __internals::drawGrid(&canvas, &graphOptions); + __internals::drawTicks(&canvas, 0, gridSize, maxValue, &graphOptions); + + for (long long i = 0; i < numbers.size(); i++) + { + const auto& dataset = numbers[i]; + + std::vector yValues; + std::vector xValues; + long long xGridLoc = graphOptions.xTickSpacing * (1 + i); + for (const auto& number : dataset) + { + long long yGridLoc = std::stoll(((maxValue - number) / gridSize).present()); + yValues.emplace_back(yGridLoc); + xValues.emplace_back(xGridLoc); + + canvas.write( + barsOptions[i].lineDot, { .x = xGridLoc, .y = 3 + yGridLoc }, false, barsOptions[i].lineColor); + for (long long x = 0; x < graphOptions.xTickSpacing; x++) + for (long long y = yGridLoc; y < graphOptions.height; y++) + canvas.write( + barsOptions[i].lineDot, { .x = xGridLoc + x, .y = 3 + y }, false, barsOptions[i].lineColor); + + xGridLoc += graphOptions.xTickSpacing * (1 + numbers.size()); + } + } + + conPlotLegend(&graphOptions, &barsOptions, &canvas); + std::cout << canvas.asString() << "\n"; + } + + void conPlot(const std::vector& f, + const GraphOptions& graphOptions, + const std::vector& linesOptions) + { + // Create buffer + prettyPrint::ConsoleOutput canvas(graphOptions.height + 10, + graphOptions.width + 12 + + steppable::__internals::stringUtils::getUnicodeDisplayWidth( + graphOptions.yAxisTitle)); // Extra space for labels + std::map> fnValues; + + // Sample min/max + Number yMin; + for (size_t fnIdx = 0; fnIdx < f.size(); ++fnIdx) + { + yMin = f[fnIdx](graphOptions.xMin); + fnValues[fnIdx][graphOptions.xMin] = yMin; + } + Number yMax = yMin; + // 1 grid = 1 character on screen + Number xGridSize = (graphOptions.xMax - graphOptions.xMin) / graphOptions.width; + + // Calculate range of values + for (size_t fnIdx = 0; fnIdx < f.size(); ++fnIdx) + { + for (long long i = 0; i <= graphOptions.width; ++i) + { + if (i % linesOptions[fnIdx].samplesSpacing == 0) + { + Number x = graphOptions.xMin + Number(i) * xGridSize; + Number y = f[fnIdx](x); + fnValues[fnIdx][x] = y; + yMin = std::min(y, yMin); + yMax = std::max(y, yMax); + } + } + + if ((yMax - yMin).abs() < 1e-12) + { + yMin -= 1.0; + yMax += 1.0; + } + } + + // Axis positions + Number yGridSize = (yMax - yMin) / graphOptions.height; + __internals::drawGrid(&canvas, &graphOptions); + __internals::drawTicks(&canvas, xGridSize, yGridSize, yMax, &graphOptions); + + // Plot function + for (size_t fnIdx = 0; fnIdx < f.size(); ++fnIdx) + __internals::conPlotLine( + xGridSize, yGridSize, yMax, &graphOptions, &linesOptions[fnIdx], &canvas, fnValues[fnIdx]); + conPlotLegend(&graphOptions, &linesOptions, &canvas); + std::cout << canvas.asString() << "\n"; + } + + void pieChart() {} +} // namespace steppable::graphing diff --git a/src/conPlot/conPlotInternals.cpp b/src/conPlot/conPlotInternals.cpp new file mode 100644 index 000000000..2b33f2fda --- /dev/null +++ b/src/conPlot/conPlotInternals.cpp @@ -0,0 +1,210 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#include "conPlot/conPlotInternals.hpp" + +#include "conPlot/conPlot.hpp" +#include "conPlot/conPlotInterpolation.hpp" +#include "conPlot/conPlotTypes.hpp" +#include "steppable/number.hpp" +#include "symbols.hpp" + +#include +#include +#include + +namespace steppable::graphing::__internals +{ + void conPlotLine(const Number& xGridSize, + const Number& yGridSize, + const Number& yMax, + const GraphOptions* graphOptions, + const LineOptions* lineOptions, + prettyPrint::ConsoleOutput* canvas, + std::map& fnValues) + { + const auto& yMin = yMax - yGridSize * graphOptions->height; + long long minShadeGrid = 0; + long long maxShadeGrid = 0; + if (lineOptions->shadeOptions != ShadeOptions::NO_SHADE) + { + auto [minShade, maxShade] = lineOptions->shadeValues; + if (minShade > maxShade) + std::swap(minShade, maxShade); + + minShadeGrid = 3 + std::stoll(((yMax - minShade) / yGridSize).present()); + maxShadeGrid = 3 + std::stoll(((yMax - maxShade) / yGridSize).present()); + } + // Plot function + std::map gridPos; + for (const auto& [i, y] : fnValues) + { + auto gridY = std::stoll(((yMax - y) / yGridSize).present()); + auto gridX = std::stoll(((graphOptions->xMax - i) / xGridSize).present()); + gridPos[gridX] = gridY; + } + if (lineOptions->samplesSpacing > 1) + cubicInterpolateFill(&gridPos, 0, graphOptions->width); + + long long lastGridY = gridPos[0]; + for (const auto& [gridX, gridY] : gridPos) + { + // Plot point + const long long diffY = gridY - lastGridY; + const long long absDiffY = std::abs(diffY); + long long sgn = 1; + if (absDiffY != 0) + sgn = diffY / absDiffY; + if (absDiffY > graphOptions->height / 2) + { + // Create more points to connect the dots + for (long long j = 1; j < absDiffY + 1; j++) + canvas->write(lineOptions->lineDot, + { .x = gridX, .y = 3 + gridY + (-sgn * j) }, + false, + lineOptions->lineColor); + } + canvas->write(lineOptions->lineDot, { .x = gridX, .y = 3 + gridY }, false, lineOptions->lineColor); + lastGridY = 3 + gridY; + + switch (lineOptions->shadeOptions) + { + case ShadeOptions::NO_SHADE: + break; + + case ShadeOptions::SHADE_BELOW_FIRST: + { + for (long long j = lastGridY - 1; j >= minShadeGrid; j--) + canvas->write(lineOptions->shadeDot, { .x = gridX, .y = j }, false, lineOptions->lineColor); + break; + } + case ShadeOptions::SHADE_ABOVE_FIRST: + { + for (long long j = lastGridY + 1; j <= minShadeGrid; j++) + canvas->write(lineOptions->shadeDot, { .x = gridX, .y = j }, false, lineOptions->lineColor); + break; + } + case ShadeOptions::SHADE_BELOW_SECOND: + { + for (long long j = lastGridY - 1; j >= maxShadeGrid; j--) + canvas->write(lineOptions->shadeDot, { .x = gridX, .y = j }, false, lineOptions->lineColor); + break; + } + case ShadeOptions::SHADE_ABOVE_SECOND: + { + for (long long j = lastGridY + 1; j <= maxShadeGrid; j++) + canvas->write(lineOptions->shadeDot, { .x = gridX, .y = j }, false, lineOptions->lineColor); + break; + } + case ShadeOptions::SHADE_BETWEEN_BOTH: + { + for (long long j = std::max(lastGridY + 1, maxShadeGrid); j <= minShadeGrid; j++) + canvas->write(lineOptions->shadeDot, { .x = gridX, .y = j }, false, lineOptions->lineColor); + break; + } + case ShadeOptions::SHADE_OUTSIDE_BOTH: + { + // Shade below first + for (long long j = lastGridY - 1; j >= minShadeGrid; j--) + canvas->write(lineOptions->shadeDot, { .x = gridX, .y = j }, false, lineOptions->lineColor); + + // Shade above second + for (long long j = lastGridY + 1; j <= maxShadeGrid; j++) + canvas->write(lineOptions->shadeDot, { .x = gridX, .y = j }, false, lineOptions->lineColor); + break; + } + default: + break; + } + } + } + + void drawTicks(prettyPrint::ConsoleOutput* canvas, + const Number& xGridSize, + const Number& yGridSize, + const Number& yMax, + const GraphOptionsBase* graphOptions) + { + // Axis positions + Number yTickValue = yMax; + + for (long long j = 0; j < graphOptions->height; ++j) + { + yTickValue -= yGridSize; + // Write y ticks + if (j % graphOptions->yTickSpacing == 0 and yGridSize != 0) + { + yTickValue.setPrec(2); + canvas->write(yTickValue.present(), { .x = graphOptions->width + 2, .y = 3 + j }); + for (long long i = 0; i < graphOptions->width; ++i) + canvas->write(BoxDrawing::DOTTED_HORIZONTAL, { .x = i, .y = 3 + j }); + canvas->write(BoxDrawing::VERTICAL_LEFT, { .x = graphOptions->width, .y = 3 + j }); + } + } + + // Draw grid + for (long long i = 0; i < graphOptions->width - 2; ++i) + { + Number x = graphOptions->xMin + Number(i) * xGridSize; + // Write grid label + if (i % graphOptions->xTickSpacing == 0 and xGridSize != 0) + { + // Write vertical gridlines + for (long long j = 0; j < graphOptions->height; ++j) + canvas->write(BoxDrawing::DOTTED_VERTICAL, { .x = i, .y = 3 + j }); + canvas->write(BoxDrawing::HORIZONTAL_UP, { .x = i, .y = 3 + graphOptions->height }); + x.setPrec(2); + canvas->write(x.present(), { .x = i, .y = 3 + graphOptions->height + 1 }); + } + } + } + + void drawGrid(prettyPrint::ConsoleOutput* canvas, const GraphOptionsBase* graphOptions) + { + canvas->write( + graphOptions->title, { .x = 0, .y = 1 }, false, formats::bold, prettyPrint::HorizontalAlignment::CENTER); + for (long long i = 0; i <= graphOptions->width; ++i) + { + // Write base frame + canvas->write(BoxDrawing::HORIZONTAL, { .x = i, .y = 3 + graphOptions->height }); + } + + // Write side frame + for (long long j = 0; j < graphOptions->height; ++j) + canvas->write(BoxDrawing::VERTICAL, { .x = graphOptions->width, .y = 3 + j }); + + // Axis Titles + canvas->write(BoxDrawing::BOTTOM_RIGHT_CORNER, { .x = graphOptions->width, .y = 3 + graphOptions->height }); + canvas->write(std::string((graphOptions->width - ::steppable::__internals::stringUtils::getUnicodeDisplayWidth( + graphOptions->xAxisTitle)) / + 2, + ' ') + + graphOptions->xAxisTitle, + { .x = 0, .y = 5 + graphOptions->height }, + false, + formats::bold); + canvas->write(graphOptions->yAxisTitle, + { .x = graphOptions->width + 10, .y = (graphOptions->height / 2) + 1 }, + false, + formats::bold); + } +} // namespace steppable::graphing::__internals diff --git a/src/conPlot/conPlotInterpolation.cpp b/src/conPlot/conPlotInterpolation.cpp new file mode 100644 index 000000000..485cd59b4 --- /dev/null +++ b/src/conPlot/conPlotInterpolation.cpp @@ -0,0 +1,148 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#include "output.hpp" + +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace steppable::graphing +{ + void linearInterpolateFill(std::map* data, long long xMin, long long xMax) + { + if (data->size() < 2) + output::error("graphing::linearInterpolateFill"s, + "At least 2 points are required for linear interpolation."s); + + std::vector xs; + std::vector ys; + for (const auto& kv : *data) + { + xs.push_back(kv.first); + ys.push_back(kv.second); + } + auto n = static_cast(xs.size()); + + for (long long x = xMin; x <= xMax; ++x) + { + // Find the interval [x0, x1] for x (or clamp to endpoints) + auto it = std::ranges::lower_bound(xs, x); + long long idx1 = std::distance(xs.begin(), it); + long long i0 = 0; + long long i1 = 0; + if (x <= xs.front()) + { + i0 = 0; + i1 = 1; + } + else if (x >= xs.back()) + { + i0 = n - 2; + i1 = n - 1; + } + else + { + i0 = idx1 - 1; + i1 = idx1; + } + + long long x0 = xs[i0]; + long long x1 = xs[i1]; + long long y0 = ys[i0]; + long long y1 = ys[i1]; + long double t = 0; + if (x1 == x0) + t = 0.0; + else + t = static_cast(x - x0) / static_cast(x1 - x0); + long long y = llround(static_cast(y0) + (t * static_cast(y1 - y0))); + (*data)[x] = y; + } + } + + void cubicInterpolateFill(std::map* data, long long xMin, long long xMax) + { + if (data->size() < 4) + linearInterpolateFill(data, xMin, xMax); + std::vector xs; + std::vector ys; + for (const auto& kv : *data) + { + xs.push_back(kv.first); + ys.push_back(kv.second); + } + auto n = static_cast(xs.size()); + + for (long long x = xMin; x <= xMax; ++x) + { + // Find the correct window of 4 points for interpolation + auto it = std::ranges::lower_bound(xs, x); + long long idx1 = std::distance(xs.begin(), it); + long long i1 = std::clamp(idx1 - 1, 1LL, n - 3); + long long i0 = i1 - 1; + long long i2 = i1 + 1; + long long i3 = i1 + 2; + + if (x <= xs[1]) + { + i0 = 0; + i1 = 1; + i2 = 2; + i3 = 3; + } + if (x >= xs[n - 2]) + { + i0 = n - 4; + i1 = n - 3; + i2 = n - 2; + i3 = n - 1; + } + + long long x0 = xs[i0]; + long long x1 = xs[i1]; + long long x2 = xs[i2]; + long long x3 = xs[i3]; + long long y0 = ys[i0]; + long long y1 = ys[i1]; + long long y2 = ys[i2]; + long long y3 = ys[i3]; + + long double t0 = static_cast((x - x1) * (x - x2) * (x - x3)) / + static_cast((x0 - x1) * (x0 - x2) * (x0 - x3)); + long double t1 = static_cast((x - x0) * (x - x2) * (x - x3)) / + static_cast((x1 - x0) * (x1 - x2) * (x1 - x3)); + long double t2 = static_cast((x - x0) * (x - x1) * (x - x3)) / + static_cast((x2 - x0) * (x2 - x1) * (x2 - x3)); + long double t3 = static_cast((x - x0) * (x - x1) * (x - x2)) / + static_cast((x3 - x0) * (x3 - x1) * (x3 - x2)); + + long long y = llround((static_cast(y0) * t0) + (static_cast(y1) * t1) + + (static_cast(y2) * t2) + (static_cast(y3) * t3)); + (*data)[x] = y; + } + } +} // namespace steppable::graphing diff --git a/src/matrix/ref/ref.cpp b/src/matrix/ref/ref.cpp index 205435ff7..5d7d65ce8 100644 --- a/src/matrix/ref/ref.cpp +++ b/src/matrix/ref/ref.cpp @@ -28,12 +28,16 @@ * @date 31st May 2025 */ +#include "conPlot/conPlot.hpp" +#include "fn/calc.hpp" #include "refReport.hpp" -#include "steppable/mat2d.hpp" #include "steppable/number.hpp" +#include "steppable/parameter.hpp" -#include #include +#include + +using namespace std::literals; namespace steppable::__internals::matrix { @@ -43,13 +47,59 @@ namespace steppable::__internals::matrix int main() { using namespace steppable; - std::vector> matrix = { { 2, 1, -1, 3, 2, 8 }, - { 1, -2, 1, 0, 2, -4 }, - { 3, 1, -3, 4, 1, 6 }, - { 1, 1, 1, 1, 1, 5 }, - { 2, -1, 2, -1, 3, 3 } }; - auto mat = steppable::Matrix(matrix); - mat = mat.rref(); - - std::cout << mat.present(1) << "\n"; + using namespace steppable::__internals::parameter; + + Utf8CodePage _; + // std::vector> matrix = { { 2, 1, -1, 3, 2, 8 }, + // { 1, -2, 1, 0, 2, -4 }, + // { 3, 1, -3, 4, 1, 6 }, + // { 1, 1, 1, 1, 1, 5 }, + // { 2, -1, 2, -1, 3, 3 } }; + // auto mat = steppable::Matrix(matrix); + // mat = mat.rref(); + + // std::cout << mat.present(1) << "\n"; + graphing::conPlot({ + [](const Number& x) { return steppable::__internals::calc::sin(x.present(), 2); }, + [](const Number& x) { return steppable::__internals::calc::cos(x.present(), 2); }, + }, + { + "width"_p = 90LL, + "xMin"_p = Number(-6.28), + "xMax"_p = Number(6.28), + "title"_p = "Sine and cosine graphs from -pi to +pi"s, + }, + { { "title"_p = "Sine"s }, + { + "lineColor"_p = (ColorFunc)colors::red, + "lineDot"_p = graphing::GraphDot::LIGHT_BLOCK_2, + "shadeDot"_p = graphing::GraphDot::LIGHT_BLOCK_1, + "title"_p = "Cosine"s, + "shadeOptions"_p = graphing::ShadeOptions::SHADE_OUTSIDE_BOTH, + "shadeValues"_p = graphing::NumberPair{ 0.1, 0.5 }, + } }); + + graphing::conPlotBar( + { + { 10, 12, 50, 12, 56, 6 }, + { 13, 14, 42, 64, 23, 10 }, + }, + { + "width"_p = 110LL, + "height"_p = 20LL, + "title"_p = "Bar plot"s, + "xAxisTitle"_p = "X axis"s, + "barWidth"_p = 5LL, + }, + { + { + "title"_p = "Bar 1"s, + "color"_p = static_cast(colors::blue), + "block"_p = graphing::GraphDot::LIGHT_BLOCK_1, + }, + { + "title"_p = "Bar 2"s, + "color"_p = static_cast(colors::red), + }, + }); } diff --git a/src/steppable/mat2d.cpp b/src/steppable/mat2d.cpp index a29dc0d4c..6882decbd 100644 --- a/src/steppable/mat2d.cpp +++ b/src/steppable/mat2d.cpp @@ -29,6 +29,7 @@ #include "steppable/mat2d.hpp" +#include "getString.hpp" #include "output.hpp" #include "platform.hpp" #include "rounding.hpp" @@ -47,6 +48,7 @@ namespace steppable { using namespace __internals; using namespace __internals::numUtils; + using namespace localization; namespace prettyPrint::printers { @@ -106,7 +108,7 @@ namespace steppable const size_t rowSize = row.size(); if (rowSize != size) { - output::error("Matrix::_checkDataSanity"s, "Matrix has non-uniform dimensions."s); + output::error("Matrix::_checkDataSanity"s, $("mat2d", "75953952-0eea-4716-a006-d0f2e7a8f6c9")); utils::programSafeExit(1); } size = rowSize; @@ -137,16 +139,16 @@ namespace steppable if (x > _cols) { - output::error("Matrix::operator[]"s, - "Incorrect x parameter. {0} exceeds the number of columns in this matrix ({1})."s, - { std::to_string(x), std::to_string(_cols) }); + output::error( + "Matrix::operator[]"s, + $("mat2d", "8d4e4757-415b-4aed-8f5e-26b3503a95dd"s, { std::to_string(x), std::to_string(_cols) })); utils::programSafeExit(1); } if (y > _rows) { - output::error("Matrix::operator[]"s, - "Incorrect y parameter. {0} exceeds the number of rows in this matrix ({1})."s, - { std::to_string(y), std::to_string(_rows) }); + output::error( + "Matrix::operator[]"s, + $("mat2d", "e7cb3f0b-11d8-4e12-8c93-4a1021b15e10"s, { std::to_string(y), std::to_string(_rows) })); utils::programSafeExit(1); } } @@ -241,11 +243,11 @@ namespace steppable MatVec2D mat = data; mat = roundOffValues(mat, static_cast(prec) + 3); - for (int col = 0, row = 0; col < _cols && row < _rows; ++col) + for (long long signed col = 0, row = 0; col < _cols && row < _rows; ++col) { // Find first non-zero in column col, at or below row - int sel = -1; - for (int i = row; i < _rows; ++i) + long long signed sel = -1; + for (long long signed i = row; i < _rows; ++i) if (mat[i][col] != 0.0) { sel = i; @@ -258,10 +260,10 @@ namespace steppable std::swap(mat[row], mat[sel]); // Swap if needed // Eliminate below - for (int i = row + 1; i < _rows; ++i) + for (long long signed i = row + 1; i < _rows; ++i) { Number factor = mat[i][col] / mat[row][col]; - for (int j = col; j < _cols; ++j) + for (long long signed j = col; j < _cols; ++j) mat[i][j] -= factor * mat[row][j]; } ++row; @@ -274,7 +276,7 @@ namespace steppable { if (_rows != _cols) { - output::error("Matrix::det"s, "Matrix is not a square."s); + output::error("Matrix::det"s, $("mat2d", "fe78bdc2-b409-4078-8e0e-313c46977f25")); utils::programSafeExit(1); } int sign = 1; @@ -323,15 +325,17 @@ namespace steppable if (rhs._cols != _cols) { output::error("Matrix::operator+"s, - "Matrix dimensions mismatch. Expect {0} columns. Got {1} columns."s, - { std::to_string(_cols), std::to_string(rhs._cols) }); + $("mat2d", + "88331f88-3a4c-4b7e-9b43-b51a1d1020e2", + { std::to_string(_cols), std::to_string(rhs._cols) })); utils::programSafeExit(1); } if (rhs._rows != _rows) { output::error("Matrix::operator+"s, - "Matrix dimensions mismatch. Expect {0} rows. Got {1} rows."s, - { std::to_string(_rows), std::to_string(rhs._rows) }); + $("mat2d", + "34e92306-a4d8-4ff0-8441-bfcd29771e94", + { std::to_string(_rows), std::to_string(rhs._rows) })); utils::programSafeExit(1); } @@ -381,11 +385,9 @@ namespace steppable { if (_cols != rhs._rows) { - output::error("Matrix::operator*"s, "Incorrect matrix dimensions for multiplication."s); + output::error("Matrix::operator*"s, $("mat2d", "17b6aadd-bce1-4558-a7cc-7a099f00e57c")); // https://en.wikipedia.org/wiki/Matrix_multiplication - output::info("Matrix::operator*"s, - "For matrix multiplication, the number of columns in the first matrix must be equal to the " - "number of rows in the second matrix"s); + output::info("Matrix::operator*"s, $("mat2d", "8966ce13-8ae9-4f14-ba4e-837b98a4c9fa")); utils::programSafeExit(1); } Matrix matrix = Matrix::zeros(_rows, rhs._cols); @@ -401,8 +403,9 @@ namespace steppable if (rhs._rows != _rows) { output::error("Matrix::operator<<"s, - "Incorrect RHS matrix dimensions. Expect {0} rows, got {1}"s, - { std::to_string(rhs._rows), std::to_string(rhs._rows) }); + $("mat2d", + "f255d307-9482-442b-a523-61a1c7465f9c", + { std::to_string(rhs._rows), std::to_string(rhs._rows) })); utils::programSafeExit(1); } @@ -425,9 +428,10 @@ namespace steppable { if (rhs._rows != _rows) { - output::error("Matrix::operator>>"s, - "Incorrect RHS matrix dimensions. Expect {0} rows, got {1}"s, - { std::to_string(rhs._rows), std::to_string(rhs._rows) }); + output::error("Matrix::operator<<"s, + $("mat2d", + "f255d307-9482-442b-a523-61a1c7465f9c", + { std::to_string(rhs._rows), std::to_string(rhs._rows) })); utils::programSafeExit(1); } diff --git a/src/steppable/number.cpp b/src/steppable/number.cpp index 86f99dd38..38f2ea552 100644 --- a/src/steppable/number.cpp +++ b/src/steppable/number.cpp @@ -47,10 +47,6 @@ namespace steppable { using namespace steppable::__internals::calc; - Number::Number() : value("0"), prec(8) {} - - Number::Number(const Number& rhs) : value(rhs.value), prec(rhs.prec) {} - Number::Number(std::string value, const size_t prec, const RoundingMode mode) : value(std::move(value)), prec(prec), mode(mode) { @@ -165,3 +161,9 @@ namespace steppable std::string Number::present() const { return value; } } // namespace steppable + +std::ostream& operator<<(std::ostream& os, const steppable::Number& number) +{ + os << number.present(); + return os; +} diff --git a/src/steppable/parameter.cpp b/src/steppable/parameter.cpp new file mode 100644 index 000000000..0014970d2 --- /dev/null +++ b/src/steppable/parameter.cpp @@ -0,0 +1,59 @@ +/************************************************************************************************** + * Copyright (c) 2023-2025 NWSOFT * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy * + * of this software and associated documentation files (the "Software"), to deal * + * in the Software without restriction, including without limitation the rights * + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included in all * + * copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * + * SOFTWARE. * + **************************************************************************************************/ + +#include "steppable/parameter.hpp" + +namespace steppable::__internals::parameter +{ + void ParameterMap::checkParameterOrder(const std::vector& names) + { + if (names.size() != values.size()) + { + output::error("ParameterMap::checkParameterOrder"s, "Incorrect name vector size."s); + utils::programSafeExit(1); + } + + for (size_t i = 0; i < values.size(); i++) + if (names[i] != values[i].name) + { + output::error("ParameterMap::checkParameterOrder"s, + "Name {0} mismatch. Expect {1}"s, + { values[i].name, names[i] }); + utils::programSafeExit(1); + } + } + + void ParameterMap::checkParameterNameUnordered(const std::vector& names) + { + if (names.size() != values.size()) + { + output::error("ParameterMap::checkParameterNameUnordered"s, "Incorrect name vector size."s); + utils::programSafeExit(1); + } + for (const auto& obj : values) + if (std::ranges::find(names, obj.name) == names.end()) + { + output::error("ParameterMap::checkParameterNameUnordered"s, "Name {0} not found."s, { obj.name }); + utils::programSafeExit(1); + } + } +} diff --git a/src/symbols.cpp b/src/symbols.cpp index 4a38c84fa..0a409aa6f 100644 --- a/src/symbols.cpp +++ b/src/symbols.cpp @@ -22,6 +22,7 @@ #include "symbols.hpp" +#include "colors.hpp" #include "util.hpp" #include @@ -36,28 +37,84 @@ namespace steppable::prettyPrint { using namespace steppable::__internals::stringUtils; + using namespace steppable::__internals::utils; ConsoleOutput::ConsoleOutput(size_t height, size_t width) : height(height), width(width) { - buffer = std::vector>(height, std::vector(width, ' ')); + buffer = std::vector(height, std::vector(width, " "s)); } - void ConsoleOutput::write(const char c, const Position& pos, bool updatePos) + void ConsoleOutput::_write(const std::string& s, + const Position& pos, + bool updatePos, + const ColorFunc& color, + const HorizontalAlignment& alignment) { - std::vector vector = buffer[pos.y]; - vector[pos.x] = c; - buffer[pos.y] = vector; + auto outputString = s; + switch (alignment) + { + case HorizontalAlignment::LEFT: + break; + case HorizontalAlignment::CENTER: + outputString = std::string((width - getUnicodeDisplayWidth(outputString)) / 2, ' ') + outputString; + break; + case HorizontalAlignment::RIGHT: + outputString = std::string(width - getUnicodeDisplayWidth(outputString), ' ') + outputString; + break; + } + + Position p = pos; + if (s == "\n") + { + p.y++; + p.x = pos.x; + return; + } + size_t stringWidth = getUnicodeDisplayWidth(outputString); + if (stringWidth <= 1) + { + std::stringstream ss; + color(ss); + ss << outputString << reset; + buffer[p.y][p.x] = ss.str(); + } + else + { + GraphemeIterator graphemeIterator(outputString); + std::string cluster; + size_t i = 0; + while (graphemeIterator.next(cluster)) + { + if (cluster == "\n") + { + i = 0; + p.y++; + p.x = pos.x; + continue; + } + std::stringstream ss; + color(ss); + ss << cluster << reset; + buffer[p.y][p.x + i] = ss.str(); + i++; + } + } if (updatePos) - curPos = pos; + curPos = p; } - void ConsoleOutput::write(const char c, const long long dLine, const long long dCol, bool updatePos) + void ConsoleOutput::write(const char c, + const long long dLine, + const long long dCol, + bool updatePos, + const ColorFunc& color, + const HorizontalAlignment& alignment) { if (dLine < 0) { // We need to add extra lines for printing at the very top - buffer.insert(buffer.begin(), -dLine, std::vector(width, ' ')); + buffer.insert(buffer.begin(), -dLine, std::vector(width, " "s)); curPos.y = 0; height -= dLine; } @@ -65,8 +122,8 @@ namespace steppable::prettyPrint { for (auto& i : buffer) { - std::vector vector = i; - vector.resize(vector.size() - dCol, ' '); + std::vector vector = i; + vector.resize(vector.size() - dCol, " "s); i = vector; } @@ -75,25 +132,16 @@ namespace steppable::prettyPrint } else curPos.x += dCol; - write(c, curPos, updatePos); + _write(std::string(1, c), curPos, updatePos, color, alignment); } - void ConsoleOutput::write(const std::string& s, const Position& pos, bool updatePos) + void ConsoleOutput::write(const char c, + const Position& pos, + bool updatePos, + const ColorFunc& color, + const HorizontalAlignment& alignment) { - Position p = pos; - for (const auto& c : s) - { - if (c == '\n') - { - p.y++; - p.x = pos.x; - continue; - } - write(c, p, false); - p.x++; - if (updatePos) - curPos = p; - } + _write(std::string(1, c), pos, updatePos, color, alignment); } std::string ConsoleOutput::asString() const @@ -113,13 +161,123 @@ namespace steppable::prettyPrint auto strings = split(s, '\n'); size_t max = 0; for (const auto& string : strings) - max = std::max(max, string.length()); + max = std::max(max, getUnicodeDisplayWidth(string)); return max; } size_t getStringHeight(const std::string& s) { return split(s, '\n').size(); } } // namespace steppable::prettyPrint +namespace steppable::__internals::stringUtils +{ + bool isZeroWidthCharacter(uint32_t codepoint) + { + return (codepoint >= 0x0300 && codepoint <= 0x036F) || // Combining diacritical marks + (codepoint >= 0x200B && codepoint <= 0x200F) || // Zero-width space, non-joiners + (codepoint == 0x2028 || codepoint == 0x2029 || + codepoint == 0x202F) || // Line separator, para separator, narrow no-break space + (codepoint >= 0xFE00 && codepoint <= 0xFE0F) || // Variation selectors + (codepoint >= 0xE0100 && codepoint <= 0xE01EF); // Supplemental variation selectors + } + + bool isEmojiBase(uint32_t cp) + { + return (cp >= 0x1F600 && cp <= 0x1F64F) || // Emoticons + (cp >= 0x1F300 && cp <= 0x1F5FF) || // Misc Symbols and Pictographs + (cp >= 0x1F680 && cp <= 0x1F6FF) || // Transport & Map + (cp >= 0x1F1E6 && cp <= 0x1F1FF) || // Regional indicators + (cp >= 0x2600 && cp <= 0x26FF) || // Misc symbols + (cp >= 0x2700 && cp <= 0x27BF) || // Dingbats + (cp >= 0x1F900 && cp <= 0x1F9FF) || // Supplemental Symbols and Pictographs + (cp >= 0x1FA70 && cp <= 0x1FAFF) || (cp == 0x200D); // ZWJ + } + + bool utf8Decode(const std::string& s, size_t& i, uint32_t& cp) + { + if (i >= s.size()) + return false; + unsigned char c = s[i]; + if (c < 0x80) + { + cp = c; + i += 1; + } + else if ((c & 0xE0) == 0xC0 && i + 1 < s.size()) + { + cp = ((c & 0x1F) << 6) | (s[i + 1] & 0x3F); + i += 2; + } + else if ((c & 0xF0) == 0xE0 && i + 2 < s.size()) + { + cp = ((c & 0x0F) << 12) | ((s[i + 1] & 0x3F) << 6) | (s[i + 2] & 0x3F); + i += 3; + } + else if ((c & 0xF8) == 0xF0 && i + 3 < s.size()) + { + cp = ((c & 0x07) << 18) | ((s[i + 1] & 0x3F) << 12) | ((s[i + 2] & 0x3F) << 6) | (s[i + 3] & 0x3F); + i += 4; + } + else + { + i += 1; + cp = 0xFFFD; + } + return true; + } + + bool isClusterExtender(uint32_t cp) + { + return (cp >= 0x1F3FB && cp <= 0x1F3FF) || // Skin tone + (cp == 0x200D) || // ZWJ + (cp == 0xFE0F) || // VS16 + isZeroWidthCharacter(cp); + } + + uint32_t getCodepoint(const std::string& str, size_t& i) + { + unsigned char c = str[i]; + if (c <= 0x7F) // Single-byte (ASCII) + return c; + if ((c & 0xE0) == 0xC0) // Two-byte sequence + return ((c & 0x1F) << 6) | (str[++i] & 0x3F); + if ((c & 0xF0) == 0xE0) // Three-byte sequence + { + unsigned char byte1 = c & 0x0F; + unsigned char byte2 = str[++i] & 0x3F; + unsigned char byte3 = str[++i] & 0x3F; + + return (byte1 << 12) | (byte2 << 6) | byte3; + } + if ((c & 0xF8) == 0xF0) // Four-byte sequence + { + unsigned char byte1 = (c & 0x07) << 18; // First byte + unsigned char byte2 = (str[++i] & 0x3F) << 12; // Second byte (increment i) + unsigned char byte3 = (str[++i] & 0x3F) << 6; // Third byte (increment i) + unsigned char byte4 = str[++i] & 0x3F; // Fourth byte (increment i) + + return byte1 | byte2 | byte3 | byte4; + } + return 0; // Invalid UTF-8 + } + + size_t getUnicodeDisplayWidth(const std::string& utf8Str) + { + size_t width = 0; + size_t len = utf8Str.size(); + for (size_t i = 0; i < len; ++i) + { + uint32_t codepoint = getCodepoint(utf8Str, i); + + if (isZeroWidthCharacter(codepoint)) + // Skip zero-width characters + continue; + width += 1; + } + + return width; + } +} // namespace steppable::__internals::stringUtils + namespace steppable::__internals::symbols { using namespace steppable::__internals::stringUtils; diff --git a/src/util.cpp b/src/util.cpp index c874ce77a..df1762482 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -204,7 +204,7 @@ namespace steppable::__internals::numUtils auto removeLeadingZeros(const std::string& string) -> std::decay_t { - if (string == "0") + if (string == "0" or string.empty()) return "0"; auto out = string; bool negative = false; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2e25b6389..a0fc1afca 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,12 +22,42 @@ ENABLE_TESTING() -FOREACH(ITEM IN LISTS TEST_TARGETS) - ADD_EXECUTABLE(${ITEM} ${ITEM}.cpp) - TARGET_LINK_LIBRARIES(${ITEM} PRIVATE func) - ADD_TEST(NAME ${ITEM} COMMAND ${CMAKE_BINARY_DIR}/tests/${ITEM}) - MESSAGE(TRACE "Added test case: ${ITEM}: ${ITEM}.cpp") +SET(TEST_TARGETS_TEMP + steppable::util + steppable::fraction + steppable::number + steppable::mat2d + steppable::factors + steppable::format + ${COMPONENTS} +) + +FOREACH(TEST_TARGET IN LISTS TEST_TARGETS_TEMP) + SET(TARGET_NAME "test") + STRING(REPLACE "::" ";" TEST_TARGET_PARTS ${TEST_TARGET}) + LIST(GET TEST_TARGET_PARTS 0 DIR_NAME) + LIST(GET TEST_TARGET_PARTS 1 FILE_NAME) + STRING(REPLACE "::" "_" TEST_TARGET ${TEST_TARGET}) + + CAPITALIZE(${DIR_NAME} DIR_NAME) + CAPITALIZE(${FILE_NAME} FILE_NAME) + CAPITALIZE(${TEST_TARGET} TEST_TARGET) + STRING(CONCAT DIR_NAME "test" ${DIR_NAME}) + STRING(CONCAT FILE_NAME "test" ${FILE_NAME}) + STRING(CONCAT TEST_TARGET "test" ${TEST_TARGET}) + + ADD_EXECUTABLE(${TEST_TARGET} ${DIR_NAME}/${FILE_NAME}.cpp) + TARGET_LINK_LIBRARIES(${TEST_TARGET} PRIVATE func) + ADD_TEST(NAME ${TEST_TARGET} COMMAND ${CMAKE_BINARY_DIR}/tests/${TEST_TARGET}) + + LIST(APPEND TEST_TARGETS ${TEST_TARGET}) + MESSAGE(TRACE "Added test case: ${TEST_TARGET}: ${DIR_NAME}/${FILE_NAME}.cpp") ENDFOREACH() +SET(TEST_TARGETS + ${TEST_TARGETS} + PARENT_SCOPE +) + ADD_CUSTOM_TARGET(tests) ADD_DEPENDENCIES(tests ${TEST_TARGETS}) diff --git a/tests/testBaseConvert.cpp b/tests/testBase/testBaseConvert.cpp similarity index 100% rename from tests/testBaseConvert.cpp rename to tests/testBase/testBaseConvert.cpp diff --git a/tests/testBaseConvert.vcxproj b/tests/testBase/testBaseConvert.vcxproj similarity index 98% rename from tests/testBaseConvert.vcxproj rename to tests/testBase/testBaseConvert.vcxproj index 516edd035..b4531fb15 100644 --- a/tests/testBaseConvert.vcxproj +++ b/tests/testBase/testBaseConvert.vcxproj @@ -1,176 +1,176 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {F29B13B1-5B1D-46D6-BC8E-B0A882BC762F} - testAdd - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - false - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - true - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - false - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - true - true - - - - - - - - {867a7b37-c961-4581-ae5b-067fd464cc7c} - - - {0a900aaa-ac12-48cf-85cf-8bb0c0130512} - - - {f6af63f8-9e0f-4da2-89b3-3096b56ba251} - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {F29B13B1-5B1D-46D6-BC8E-B0A882BC762F} + testAdd + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + false + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + false + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + true + true + + + + + + + + {867a7b37-c961-4581-ae5b-067fd464cc7c} + + + {0a900aaa-ac12-48cf-85cf-8bb0c0130512} + + + {f6af63f8-9e0f-4da2-89b3-3096b56ba251} + + + + + \ No newline at end of file diff --git a/tests/testDecimalConvert.cpp b/tests/testBase/testDecimalConvert.cpp similarity index 100% rename from tests/testDecimalConvert.cpp rename to tests/testBase/testDecimalConvert.cpp diff --git a/tests/testDecimalConvert.vcxproj b/tests/testBase/testDecimalConvert.vcxproj similarity index 98% rename from tests/testDecimalConvert.vcxproj rename to tests/testBase/testDecimalConvert.vcxproj index 56fc63aef..9acaa70fe 100644 --- a/tests/testDecimalConvert.vcxproj +++ b/tests/testBase/testDecimalConvert.vcxproj @@ -1,176 +1,176 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {F74A691E-785E-43F1-9D99-6474918BD7DE} - testAdd - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - false - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - true - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - false - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - true - true - - - - - - - - {867a7b37-c961-4581-ae5b-067fd464cc7c} - - - {0a900aaa-ac12-48cf-85cf-8bb0c0130512} - - - {f6af63f8-9e0f-4da2-89b3-3096b56ba251} - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {F74A691E-785E-43F1-9D99-6474918BD7DE} + testAdd + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + false + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + false + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + true + true + + + + + + + + {867a7b37-c961-4581-ae5b-067fd464cc7c} + + + {0a900aaa-ac12-48cf-85cf-8bb0c0130512} + + + {f6af63f8-9e0f-4da2-89b3-3096b56ba251} + + + + + \ No newline at end of file diff --git a/tests/testAbs.cpp b/tests/testCalc/testAbs.cpp similarity index 100% rename from tests/testAbs.cpp rename to tests/testCalc/testAbs.cpp diff --git a/tests/testAbs.vcxproj b/tests/testCalc/testAbs.vcxproj similarity index 98% rename from tests/testAbs.vcxproj rename to tests/testCalc/testAbs.vcxproj index eef79804b..3492adb03 100644 --- a/tests/testAbs.vcxproj +++ b/tests/testCalc/testAbs.vcxproj @@ -1,166 +1,166 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {E84B0149-B3A3-41AA-AC7B-FC13A7FB02D8} - testAdd - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - false - - - true - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - false - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(SolutionDir)\include - stdcpp20 - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(SolutionDir)\include - stdcpp20 - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(SolutionDir)\include - stdcpp20 - /utf-8 %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - $(SolutionDir)\include - stdcpp20 - - - Console - true - true - true - - - - - {0a900aaa-ac12-48cf-85cf-8bb0c0130512} - - - {f6af63f8-9e0f-4da2-89b3-3096b56ba251} - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {E84B0149-B3A3-41AA-AC7B-FC13A7FB02D8} + testAdd + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)\include + stdcpp20 + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)\include + stdcpp20 + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)\include + stdcpp20 + /utf-8 %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)\include + stdcpp20 + + + Console + true + true + true + + + + + {0a900aaa-ac12-48cf-85cf-8bb0c0130512} + + + {f6af63f8-9e0f-4da2-89b3-3096b56ba251} + + + + + + + + \ No newline at end of file diff --git a/tests/testAdd.cpp b/tests/testCalc/testAdd.cpp similarity index 100% rename from tests/testAdd.cpp rename to tests/testCalc/testAdd.cpp diff --git a/tests/testAdd.vcxproj b/tests/testCalc/testAdd.vcxproj similarity index 100% rename from tests/testAdd.vcxproj rename to tests/testCalc/testAdd.vcxproj diff --git a/tests/testAtan2.cpp b/tests/testCalc/testAtan2.cpp similarity index 100% rename from tests/testAtan2.cpp rename to tests/testCalc/testAtan2.cpp diff --git a/tests/testComparison.cpp b/tests/testCalc/testComparison.cpp similarity index 100% rename from tests/testComparison.cpp rename to tests/testCalc/testComparison.cpp diff --git a/tests/testComparison.vcxproj b/tests/testCalc/testComparison.vcxproj similarity index 100% rename from tests/testComparison.vcxproj rename to tests/testCalc/testComparison.vcxproj diff --git a/tests/testDivision.cpp b/tests/testCalc/testDivision.cpp similarity index 100% rename from tests/testDivision.cpp rename to tests/testCalc/testDivision.cpp diff --git a/tests/testDivision.vcxproj b/tests/testCalc/testDivision.vcxproj similarity index 100% rename from tests/testDivision.vcxproj rename to tests/testCalc/testDivision.vcxproj diff --git a/tests/testFactorial.cpp b/tests/testCalc/testFactorial.cpp similarity index 100% rename from tests/testFactorial.cpp rename to tests/testCalc/testFactorial.cpp diff --git a/tests/testHyp.cpp b/tests/testCalc/testHyp.cpp similarity index 100% rename from tests/testHyp.cpp rename to tests/testCalc/testHyp.cpp diff --git a/tests/testLog.cpp b/tests/testCalc/testLog.cpp similarity index 100% rename from tests/testLog.cpp rename to tests/testCalc/testLog.cpp diff --git a/tests/testMultiply.cpp b/tests/testCalc/testMultiply.cpp similarity index 100% rename from tests/testMultiply.cpp rename to tests/testCalc/testMultiply.cpp diff --git a/tests/testMultiply.vcxproj b/tests/testCalc/testMultiply.vcxproj similarity index 100% rename from tests/testMultiply.vcxproj rename to tests/testCalc/testMultiply.vcxproj diff --git a/tests/testPower.cpp b/tests/testCalc/testPower.cpp similarity index 100% rename from tests/testPower.cpp rename to tests/testCalc/testPower.cpp diff --git a/tests/testPower.vcxproj b/tests/testCalc/testPower.vcxproj similarity index 98% rename from tests/testPower.vcxproj rename to tests/testCalc/testPower.vcxproj index 8c8ac6560..77a344e46 100644 --- a/tests/testPower.vcxproj +++ b/tests/testCalc/testPower.vcxproj @@ -1,173 +1,173 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {AD080EEA-19BA-4944-B8ED-2B21F1E5F0D7} - testAdd - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - false - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - true - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - false - $(SolutionDir)$(Configuration)\ - $(SolutionDir)$(Configuration)\$(ProjectName).build\ - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - $(SolutionDir)\include - /utf-8 %(AdditionalOptions) - - - Console - true - true - true - - - - - {0a900aaa-ac12-48cf-85cf-8bb0c0130512} - - - {f6af63f8-9e0f-4da2-89b3-3096b56ba251} - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {AD080EEA-19BA-4944-B8ED-2B21F1E5F0D7} + testAdd + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + false + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + false + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\$(ProjectName).build\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + $(SolutionDir)\include + /utf-8 %(AdditionalOptions) + + + Console + true + true + true + + + + + {0a900aaa-ac12-48cf-85cf-8bb0c0130512} + + + {f6af63f8-9e0f-4da2-89b3-3096b56ba251} + + + + + + + + \ No newline at end of file diff --git a/tests/testRoot.cpp b/tests/testCalc/testRoot.cpp similarity index 100% rename from tests/testRoot.cpp rename to tests/testCalc/testRoot.cpp diff --git a/tests/testRoot.vcxproj b/tests/testCalc/testRoot.vcxproj similarity index 100% rename from tests/testRoot.vcxproj rename to tests/testCalc/testRoot.vcxproj diff --git a/tests/testSubtract.cpp b/tests/testCalc/testSubtract.cpp similarity index 100% rename from tests/testSubtract.cpp rename to tests/testCalc/testSubtract.cpp diff --git a/tests/testSubtract.vcxproj b/tests/testCalc/testSubtract.vcxproj similarity index 100% rename from tests/testSubtract.vcxproj rename to tests/testCalc/testSubtract.vcxproj diff --git a/tests/testTrig.cpp b/tests/testCalc/testTrig.cpp similarity index 100% rename from tests/testTrig.cpp rename to tests/testCalc/testTrig.cpp diff --git a/tests/testTrig.vcxproj b/tests/testCalc/testTrig.vcxproj similarity index 100% rename from tests/testTrig.vcxproj rename to tests/testCalc/testTrig.vcxproj diff --git a/tests/testNInt.cpp b/tests/testCalculus/testNInt.cpp similarity index 100% rename from tests/testNInt.cpp rename to tests/testCalculus/testNInt.cpp diff --git a/tests/testRef.cpp b/tests/testMatrix/testRef.cpp similarity index 100% rename from tests/testRef.cpp rename to tests/testMatrix/testRef.cpp diff --git a/tests/testFactors.cpp b/tests/testSteppable/testFactors.cpp similarity index 100% rename from tests/testFactors.cpp rename to tests/testSteppable/testFactors.cpp diff --git a/tests/testFactors.vcxproj b/tests/testSteppable/testFactors.vcxproj similarity index 100% rename from tests/testFactors.vcxproj rename to tests/testSteppable/testFactors.vcxproj diff --git a/tests/testFormat.cpp b/tests/testSteppable/testFormat.cpp similarity index 100% rename from tests/testFormat.cpp rename to tests/testSteppable/testFormat.cpp diff --git a/tests/testFraction.cpp b/tests/testSteppable/testFraction.cpp similarity index 100% rename from tests/testFraction.cpp rename to tests/testSteppable/testFraction.cpp diff --git a/tests/testMat2d.cpp b/tests/testSteppable/testMat2d.cpp similarity index 100% rename from tests/testMat2d.cpp rename to tests/testSteppable/testMat2d.cpp diff --git a/tests/testNumber.cpp b/tests/testSteppable/testNumber.cpp similarity index 100% rename from tests/testNumber.cpp rename to tests/testSteppable/testNumber.cpp diff --git a/tests/testUtil.cpp b/tests/testSteppable/testUtil.cpp similarity index 100% rename from tests/testUtil.cpp rename to tests/testSteppable/testUtil.cpp diff --git a/tests/testUtil.vcxproj b/tests/testSteppable/testUtil.vcxproj similarity index 100% rename from tests/testUtil.vcxproj rename to tests/testSteppable/testUtil.vcxproj