diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..c055c99 --- /dev/null +++ b/.clangd @@ -0,0 +1,4 @@ +CompileFlags: # Tweak the parse settings + Add: [-xc++, -Wall, -std=c++23, -stdlib=libc++, --target=x86_64-w64-windows-gnu] # treat all files as C++, enable more warnings + Remove: -W* # strip all other warning-related flags + Compiler: clang++ # Change argv[0] of compile flags to `clang++` diff --git a/.github/workflows/project_tests.yml b/.github/workflows/project_tests.yml index 2a4accb..987414d 100644 --- a/.github/workflows/project_tests.yml +++ b/.github/workflows/project_tests.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: include: - - { os: ubuntu-latest } + - { os: ubuntu-latest, clang_version: 19 } # - { os: macos-latest } # - { os: windows-latest } @@ -22,16 +22,32 @@ jobs: - uses: actions/checkout@v3 - name: Clone Zork++ - uses: GuillaumeFalourd/clone-github-repo-action@v2 + uses: GuillaumeFalourd/clone-github-repo-action@v2.3 with: owner: 'ZeroDayCode' repository: 'Zork' + branch: 'feature/GH-127-clang-upgrades' - - name: Download the latest possible version of Clang + - name: Installing LLVM ${{ matrix.clang_version }} run: | - sudo apt update - sudo apt install clang-15 --install-suggests - sudo apt install libc++-15-dev libc++abi-15-dev + # Exit on error + set -e + # Download and execute the LLVM installation script for the specified Clang version + echo "-----> Downloading and executing the LLVM installation script for Clang ${{ matrix.clang_version }}" + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh ${{ matrix.clang_version }} + + echo "-----> Installing libc++" + sudo apt-get install -y libc++-${{ matrix.clang_version }}-dev libc++abi-${{ matrix.clang_version }}-dev libunwind-${{ matrix.clang_version }} libunwind-${{ matrix.clang_version }}-dev libc6 libzstd1 + + # Update the symbolic link to point to the newly installed Clang version + echo "-----> Updating the symbolic link to point to Clang ${{ matrix.clang_version }}" + sudo rm -f /usr/bin/clang++ + sudo ln -s /usr/bin/clang++-${{ matrix.clang_version }} /usr/bin/clang++ + + # Find the system libc++ installations + find /usr -type f \( -name 'std.cppm' -o -name 'std.compat.cppm' \) -exec echo "Found: {}" \; - name: Generate a Zork++ release build run: | @@ -42,4 +58,4 @@ jobs: cp ./Zork/zork++/target/release/zork++ . - name: Running the tests for ${{ matrix.os }} with Zork++ - run: ./zork++ --match-files gh_linux -vv -c run \ No newline at end of file + run: ./zork++ --match-files gh_linux -vv -c test diff --git a/.gitignore b/.gitignore index f5c0f37..f01243b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,11 +3,7 @@ data.txt out/ .idea/ gcm.cache/ -CMakeLists.txt -cmake-build-debug -.clangd compile_commands.json -notes.txt +zork *.exe .cache -.clang-format diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/zero/ifc/commons/concepts.cppm b/zero/ifc/commons/concepts.cppm index 9084504..5b67154 100644 --- a/zero/ifc/commons/concepts.cppm +++ b/zero/ifc/commons/concepts.cppm @@ -12,6 +12,22 @@ import type_traits; export namespace zero::concepts { + /// \brief Constrain for types that has an operator<< overload + template + concept Ostreamable = requires(const T& t, std::ostream& os) { + { os << t } -> std::same_as; + }; + + /// \brief Constrain for types that has an std::to_string implementation + template + concept StringConvertible = requires(const T& t) { + { std::to_string(t) } -> std::same_as; + }; + + /// \brief Constrain for types with either operator<< or std::to_string + template + concept Printable = Ostreamable || StringConvertible; + /** * @brief checks if the type is a type with a push_back * member method implemented diff --git a/zero/ifc/commons/typedefs.cppm b/zero/ifc/commons/typedefs.cppm index 3c05022..0b7c6b6 100644 --- a/zero/ifc/commons/typedefs.cppm +++ b/zero/ifc/commons/typedefs.cppm @@ -1,7 +1,7 @@ /** * @brief Provides a custom way of reexport common types and symbols typically * found on some system headers in a convenient and encapsulated way. - * + * * The main motivation of this module interface unit is to wrap all the system * headers that has standard C++ typedefs, avoding the user to have to include * on every file that is required those definitions, that also must be defined @@ -14,45 +14,43 @@ * Defined in header * Defined in header * Defined in header - * - * Defined in header (since C++17) + * + * Defined in header (since C++17) * ``` */ export module typedefs; -#ifdef __clang__ +#if defined(__clang__) || defined(_MSC_VER) import std; #elif defined(__GNUC__) import ; -#elif defined(_MSC_VER) - import std.core; #endif export namespace zero { /** - * @brief `zero::size_t` is a typedef for wrapping the `std::size_t`, which is the unsigned integer type of the result + * @brief `zero::size_t` is a typedef for wrapping the `std::size_t`, which is the unsigned integer type of the result * of the sizeof operator as well as the sizeof... operator and the alignof operator. * The bit width of std::size_t is not less than 16. * zero::size_t can store the maximum size of a theoretically possible object of any type (including array). - * A type whose size cannot be represented by zero::size_t is ill-formed. On many platforms + * A type whose size cannot be represented by zero::size_t is ill-formed. On many platforms * (an exception is systems with segmented addressing) zero::size_t can safely store the value of any non-member pointer, - * in which case it is synonymous with std::uintptr_t. + * in which case it is synonymous with std::uintptr_t. */ typedef std::size_t size_t; /** - * @brief zero::ptrdiff_t is a type alias for the std::ptrdiff_t, + * @brief zero::ptrdiff_t is a type alias for the std::ptrdiff_t, * being the signed integer type of the result of subtracting two pointers. - * - * is used for pointer arithmetic and array indexing, if negative values are - * possible. + * + * is used for pointer arithmetic and array indexing, if negative values are + * possible. * Programs that use other types, such as int, may fail on, e.g. 64-bit systems * when the index exceeds INT_MAX or if it relies on 32-bit modular arithmetic - * + * * Most common place of use if usually working with iterators, specially to fullfil * the `difference_type` template argument */ diff --git a/zero/ifc/iterators/internal/iterator_detail.cpp b/zero/ifc/iterators/internal/iterator_detail.cpp index 59d26e8..2ae7db1 100644 --- a/zero/ifc/iterators/internal/iterator_detail.cpp +++ b/zero/ifc/iterators/internal/iterator_detail.cpp @@ -17,13 +17,13 @@ namespace iterator::__detail { struct arrow_proxy { Reference r; - arrow_proxy(Reference&& value) : r(std::move(value)) {} + explicit arrow_proxy(Reference&& value) : r(std::move(value)) {} Reference* operator->() const { return &r; } }; /** * @brief The iterator declares itself a single pass iterator. - * input or output iterators must meet this requirement + * input or output iterators must meet this requirement */ template concept is_single_pass = bool(T::single_pass_iterator); @@ -62,7 +62,7 @@ namespace iterator::__detail { template using infer_difference_type_t = typename infer_difference_type::type; - /// Partial template speciallizations to allow us to deduce the + /// Partial template specializations to allow us to deduce the /// iterator mandatory `value:type` template requires requires { typename T::value_type; } @@ -102,7 +102,7 @@ namespace iterator::__detail { /// Helper concept to declare a later-deduced parameter type, /// but that type is still constrained to be a type that we - ///don’t yet know + /// don’t yet know template concept difference_type_arg = std::convertible_to>; diff --git a/zero/ifc/iterators/iterator.cppm b/zero/ifc/iterators/iterator.cppm index 0544119..2f52643 100644 --- a/zero/ifc/iterators/iterator.cppm +++ b/zero/ifc/iterators/iterator.cppm @@ -5,7 +5,7 @@ export module iterator; -export import :concepts; +export import :concepts; ///< Iterator concepts export import :input_iterator; export import :iterator_facade; diff --git a/zero/ifc/iterators/iterator_facade.cppm b/zero/ifc/iterators/iterator_facade.cppm index 4067753..7faa5e3 100644 --- a/zero/ifc/iterators/iterator_facade.cppm +++ b/zero/ifc/iterators/iterator_facade.cppm @@ -8,15 +8,15 @@ import :detail; import std; export namespace zero::iterator { -/** - * @brief CRTP base class for provide an iterator facade that quickly - * allows the user to build any kind of iterator - */ -template + /** + * @brief CRTP base class for provide an iterator facade that quickly + * allows the user to build any kind of iterator + */ + template class iterator_facade { private: friend Derived; - iterator_facade() {} + iterator_facade() = default; auto _self() -> Derived& { return static_cast(*this); @@ -147,7 +147,7 @@ template * the things on the standard that expects some properties from an iterator, * we must specialize `std::iterator_traits` for our facade. * - * Why? For example, when some algorythm will request for + * Why? For example, when some algorithm will request for * `iterator_implementation::value_type`, the implementor will get errors, * because there's no `value_type` type member. * @@ -157,7 +157,7 @@ template * `self_type::value_type`, and we can't know it until after the full definition * of `self_type`. * - * This is a classical know issue with the `CRTP` idiom, the derived types are uncomplete + * This is a classical know issue with the `CRTP` idiom, the derived types are uncompleted * types until is full definition. Luckily, the standard allows to specialize * `std::iterator_traits` for user defined types, so the specialization below is a * partial specialization for any class that is derived from our facade and specialized diff --git a/zero/ifc/iterators/legacy/legacy_output_iterator.cppm b/zero/ifc/iterators/legacy/legacy_output_iterator.cppm index 5eb217a..722a862 100644 --- a/zero/ifc/iterators/legacy/legacy_output_iterator.cppm +++ b/zero/ifc/iterators/legacy/legacy_output_iterator.cppm @@ -16,7 +16,7 @@ export namespace zero::iterator::legacy template struct output_iter : base_it { private: - T *_elem; + T *_ptr; // TODO move this to the details partition /** @@ -29,7 +29,7 @@ export namespace zero::iterator::legacy struct output_iter_proxy { output_iter &_iter; - constexpr output_iter_proxy(output_iter &iter) noexcept : _iter(iter) {} + constexpr explicit output_iter_proxy(output_iter &iter) noexcept : _iter(iter) {} template constexpr auto operator=(const U &val) -> output_iter_proxy& { @@ -49,8 +49,8 @@ export namespace zero::iterator::legacy }; public: - constexpr output_iter() noexcept = default; - constexpr output_iter(T &elem) noexcept : _elem(&elem) {} + constexpr output_iter() noexcept = default; // Left defaulted and not deleted because the legacy implementation + constexpr explicit output_iter(T& elem) noexcept : _ptr(&elem) {} constexpr output_iter(const output_iter &other) = default; constexpr output_iter(output_iter &&other) noexcept = default; @@ -58,11 +58,11 @@ export namespace zero::iterator::legacy template constexpr auto operator=(const U &val) -> output_iter & { if constexpr (std::is_base_of_v>) - *_elem << val; + *_ptr << val; else if constexpr (zero::concepts::has_push_back, const U &>) - _elem->push_back(val); + _ptr->push_back(val); else - *_elem++ = val; + *_ptr++ = val; return *this; } @@ -88,8 +88,7 @@ export namespace zero::iterator::legacy [[nodiscard]] constexpr friend auto operator!=(const output_iter& lhs, const output_iter& rhs) noexcept -> bool { - return not (lhs == rhs); + return lhs != rhs; } }; - -} \ No newline at end of file +} diff --git a/zero/ifc/math/README.md b/zero/ifc/math/README.md index f58bd3a..505c513 100644 --- a/zero/ifc/math/README.md +++ b/zero/ifc/math/README.md @@ -4,6 +4,17 @@ This library aims to provide mathematical operations, operators and symbols, for in standalone mathematical operations or in more complex environments, like solving problems in diverse science fields. +## Numbers +This module brings certain types for represents and operate with some sets of numbers that exists in maths. + - Natural Numbers (ℕ) + - Integers (ℤ) + - Rationals (ℚ) + - Irrationals ( /* Not yet implemented */ ) + - Reals (ℝ) + - ComplexNumbers (ℂ) + - Quaternions (ℍ) + - Primes (ℙ) + ## Ops TODO diff --git a/zero/ifc/math/general.cppm b/zero/ifc/math/general.cppm new file mode 100644 index 0000000..3e989c1 --- /dev/null +++ b/zero/ifc/math/general.cppm @@ -0,0 +1,47 @@ +/** + * @brief Contains general types/concepts/operations that are meant to be + * shared across the multiple partitions of the module math + */ + +export module math:general; + +import std; +import math.symbols; + +export namespace zero::math { + /// Compile time constants for represent an arithmetic operation via literal constants + enum class ArithmeticOperation { + Add, + Subtract, + Multiply, + Divide + }; + + /// Forward declarations of the 'Numbers types', so they can be shared across different + /// module partitions of 'math' + class Natural; + class Integer; + class Rational; + class Irrational; + template class Real; + class Complex; + + /// Concept to act as an interface for the abstract concept of 'number' in mathematics. + /// In particular, this interface represents a kind of number that belongs to a concrete set of numbers, + /// for example, the naturals, the integers, the reals, the complex numbers... + template + concept Number = (( + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v> || + std::is_same_v + ) && requires { + T::symbol; /* Check if 'T' has a static member named 'symbol' */ + { T::symbol } -> std::same_as; // Check if 'T::symbol' has the type MathSymbol + } ); + + template + concept Numerical = Number> || std::is_arithmetic_v>; +} diff --git a/zero/ifc/math/linear_algebra/matrix.cppm b/zero/ifc/math/linear_algebra/matrix.cppm index 8d677cb..7dc6c10 100644 --- a/zero/ifc/math/linear_algebra/matrix.cppm +++ b/zero/ifc/math/linear_algebra/matrix.cppm @@ -76,11 +76,11 @@ export { Matrix() = delete; /// Row Matrix constructor - constexpr Matrix( + constexpr Matrix( std::initializer_list rows ) requires RowMatrix : data {rows} {} /// Column Matrix constructor - constexpr Matrix( + constexpr Matrix( std::initializer_list columns ) requires ColumnMatrix : data {columns} {} diff --git a/zero/ifc/math/math.cppm b/zero/ifc/math/math.cppm index 253672e..166629c 100644 --- a/zero/ifc/math/math.cppm +++ b/zero/ifc/math/math.cppm @@ -4,9 +4,10 @@ * This module serves as the public interface for the mathematical * types and operations defined in the library. */ - export module math; +export import :general; export import math.ops; +export import :numbers; export import math.symbols; -export import math.linear_algebra; \ No newline at end of file +export import math.linear_algebra; diff --git a/zero/ifc/math/numbers.cppm b/zero/ifc/math/numbers.cppm deleted file mode 100644 index 9605e5a..0000000 --- a/zero/ifc/math/numbers.cppm +++ /dev/null @@ -1,284 +0,0 @@ -/// This module provides strong types over the most common sets of numbers in mathematics - -export module math.numbers; - -import std; -import math.ops; -import math.symbols; - -export namespace zero::math { - // Forward declarations - class Natural; - class Integer; - class Rational; - class Irrational; - class Real; - class Complex; - - /// Concept to act as an interface for the abstract concept of 'number' in mathematics. - /// In particular, this interface represents a kind of number that belongs to a concrete set of numbers, - /// for example, the naturals, the integers, the reals, the complex numbers... - template - concept Number = ( - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v - ) && requires { - T::symbol; /* Check if 'T' has a static member named 'symbol' */ - { T::symbol } -> std::same_as; // Check if 'T::symbol' has the type MathSymbol - }; - - // TODO: Create individual concepts per Number type that allows to check more complex behaviour, - // like overflows (is this possible with a concept??!), that they can be constructible from certain types - // which allows us to reduce to only one template constructor per type instead of having lots of them - - /// A positive integer number - class Natural { - private: - unsigned int _number; - public: - static constexpr MathSymbol symbol { MathSymbol::Naturals }; - - [[nodiscard]] constexpr explicit Natural(unsigned int value) noexcept : _number(value) {} - - /// @return an {@link unsigned int}, which is the value stored in the type, being only a positive integer number - [[nodiscard]] inline constexpr unsigned int number() const noexcept { return _number; } - - // Arithmetic operator overloads - [[nodiscard]] inline constexpr Natural operator+(Natural rhs) const noexcept; - [[nodiscard]] inline constexpr Natural operator-(Natural rhs) const noexcept; - [[nodiscard]] inline constexpr Natural operator*(Natural rhs) const noexcept; - [[nodiscard]] inline constexpr Rational operator/(Natural rhs) const noexcept; - // Comparison operator overloads - [[nodiscard]] inline constexpr bool operator==(Natural rhs) const noexcept; - [[nodiscard]] inline constexpr bool operator==(unsigned int rhs) const noexcept; - // Printable // TODO: please, add a concept for this operators - inline constexpr friend std::ostream& operator<<(std::ostream& os, const Natural& rhs) { - os << rhs._number; - return os; - } - }; - - /// A whole (non decimal nor fraction) real number - class Integer { - private: - signed int _number; - public: - static constexpr MathSymbol symbol { MathSymbol::Integers }; - - [[nodiscard]] constexpr explicit Integer(signed int value) noexcept : _number(value) {} - [[nodiscard]] constexpr explicit Integer(const Natural value) noexcept - : _number(static_cast(value.number())) {} - - /// @return a {@link signed int}, which is the value stored in the type, being a whole number (integer) - [[nodiscard]] inline constexpr signed int number() const noexcept { return _number; } - - // Arithmetic operator overloads - [[nodiscard]] inline constexpr Integer operator+(Integer rhs) const noexcept; - [[nodiscard]] inline constexpr Integer operator-(Integer rhs) const noexcept; - [[nodiscard]] inline constexpr Integer operator*(Integer rhs) const noexcept; - [[nodiscard]] inline constexpr Rational operator*(Rational rhs) const noexcept; - [[nodiscard]] inline constexpr Rational operator/(Integer rhs) const noexcept; // TODO: this can't be noexcept - // Comparison operator overloads - [[nodiscard]] inline constexpr bool operator==(Integer rhs) const noexcept; - [[nodiscard]] inline constexpr bool operator==(int rhs) const noexcept; - - // Explicit conversion operators - [[nodiscard]] inline explicit operator int() const { return _number; } - // Printable - inline constexpr friend std::ostream& operator<<(std::ostream& os, const Integer& rhs) { - os << rhs._number; - return os; - } - }; - - /// @brief A type that represents rational numbers of the form: ℚ = {a, b ∈ ℤ, b ≠ 0} - /// - /// The Rational class encapsulates a fraction with an integer numerator and denominator. - /// Rational numbers are represented as ratios of integers, where the numerator belongs to ℤ - /// (the set of integers) and the denominator belongs to ℤ excluding zero. - /// - /// @note The class uses the Integer type to represent both the numerator and denominator. - /// - /// Example usage: - /// @code - /// Rational r1(1, 2); // Represents the fraction 1/2 - /// Rational r2(3, -4); // Represents the fraction -3/4 - /// Rational r; // Forbidden. Default constructor is not defined. Compile time error. - /// @endcode - /// - /// @apiNote The rational type allows the construction of fractions which are undefined, like (x, 0) x/0, - /// where the denominator is equals to zero, or even 0/0, and the operations are not checked, which this will - /// lead directly to **undefined behaviour** - class Rational { - private: - Integer _numerator; ///< The numerator of the rational number, belonging to ℤ. - Integer _denominator; ///< The denominator of the rational number, belonging to ℤ, NOT excluding the zero. - public: - static constexpr MathSymbol symbol = MathSymbol::Rationals; - - [[nodiscard]] constexpr Rational(int numerator, int denominator) noexcept - : _numerator(numerator), _denominator(denominator) {} - - [[nodiscard]] constexpr Rational(Natural numerator, Natural denominator) noexcept - : _numerator(static_cast(numerator)), _denominator(static_cast(denominator)) {} - - [[nodiscard]] constexpr Rational(Integer numerator, Integer denominator) noexcept - : _numerator(numerator), _denominator(denominator) {} - - /// @return a {@link Integer} with the value of the numerator for this rational - [[nodiscard]] inline constexpr Integer numerator() const noexcept { return _numerator; } - - /// @return a {@link Integer} with the value of the denominator for this rational - [[nodiscard]] inline constexpr Integer denominator() const noexcept { return _denominator; } - - // TODO Add a method to reduce fractions - - // Arithmetic operator overloads - [[nodiscard]] inline constexpr Rational operator+(const Rational rhs) const; - [[nodiscard]] inline constexpr Rational operator-(const Rational rhs) const; - [[nodiscard]] inline constexpr Rational operator*(const Integer rhs) const; - [[nodiscard]] inline constexpr Rational operator*(const Rational rhs) const; - - // TODO complete arithmetic overloads - // Comparison operator overloads - [[nodiscard]] inline constexpr bool operator==(Rational rhs) const noexcept; - - // Printable - friend std::ostream &operator<<(std::ostream& os, const Rational& rhs) { - os << rhs._numerator; - os << MathSymbol::DivisionSlash; - os << rhs._denominator; - return os; - } - - private: // TODO: move to an standalone helper - [[nodiscard]] constexpr Rational sum_or_subtract(const Rational &rhs, int sign) const; - }; - -// class Real { -// double number; // TODO handle rationals and irrationals with std::variant? -// }; -// -// class Complex { -// Real real; -// Real imaginary; -// }; - -} - -// TODO move this ones to an internal module partition?? or to a module implementation better? -using namespace zero::math; - - /*++++++++ Operator overloads implementations ++++++++++*/ - -/*+++++++++++++++++ Naturals +++++++++++++++++*/ -// Arithmetic -[[nodiscard]] inline constexpr Natural Natural::operator+(const Natural rhs) const noexcept { - return Natural(_number + rhs.number()); -} -/// TODO should we do something about the values < 1? -[[nodiscard]] inline constexpr Natural Natural::operator-(const Natural rhs) const noexcept { - return Natural(_number - rhs.number()); -} - -[[nodiscard]] inline constexpr Natural Natural::operator*(const Natural rhs) const noexcept { - return Natural(_number * rhs.number()); -} -[[nodiscard]] inline constexpr Rational Natural::operator/(const Natural rhs) const noexcept { - return {static_cast(_number), static_cast(rhs.number())}; -} -// Equality -[[nodiscard]] inline constexpr bool Natural::operator==(const Natural rhs) const noexcept { - return _number == rhs.number(); -} -[[nodiscard]] inline constexpr bool Natural::operator==(const unsigned int rhs) const noexcept { - return _number == rhs; -} - -/*+++++++++++++++++ Integers +++++++++++++++++*/ -// Arithmetic -[[nodiscard]] inline constexpr Integer Integer::operator+(const Integer rhs) const noexcept { - return Integer(_number + rhs.number()); -} -[[nodiscard]] inline constexpr Integer Integer::operator-(const Integer rhs) const noexcept { - return Integer(_number - rhs.number()); -} -[[nodiscard]] inline constexpr Integer Integer::operator*(const Integer rhs) const noexcept { - return Integer(_number * rhs.number()); -} -[[nodiscard]] inline constexpr Rational Integer::operator*(const Rational rhs) const noexcept { - return {_number * rhs.numerator().number(), rhs.denominator().number()}; -} -[[nodiscard]] inline constexpr Rational Integer::operator/(const Integer rhs) const noexcept { // TODO: wrong impl, this always should return a Rational? - return {static_cast(_number), static_cast(rhs.number())}; -} -// Equality -[[nodiscard]] inline constexpr bool Integer::operator==(const Integer rhs) const noexcept { - return _number == rhs.number(); -} -[[nodiscard]] inline constexpr bool Integer::operator==(const int rhs) const noexcept { - return _number == rhs; -} - - /*+++++++++++++++++ Rationals +++++++++++++++++*/ -// Arithmetic - -// Addition operator -[[nodiscard]] constexpr Rational Rational::operator+(const Rational rhs) const { - return this->sum_or_subtract(rhs, 1); -} -// Subtraction operator -[[nodiscard]] constexpr Rational Rational::operator-(const Rational rhs) const { - return this->sum_or_subtract(rhs, -1); -} -[[nodiscard]] constexpr Rational Rational::operator*(const Integer rhs) const { - return Rational(_numerator * rhs, _denominator); -} -[[nodiscard]] constexpr Rational Rational::operator*(const Rational rhs) const { - return Rational( - _numerator * rhs.numerator(), _denominator * rhs.denominator() - ); -} - -/// Private helper function to perform the common logic for addition and subtraction -/// @param rhs The rational number to be added or subtracted. -/// \param sign -/// @return The sum of the two rational numbers. -/// -/// This method handles both like and unlike fractions. If the denominators of -/// the two fractions are equal, it directly adds the numerators. Otherwise, it -/// finds the least common multiple (LCM) of the denominators and scales the -/// numerators to have the LCM as the common denominator before adding. -// TODO: move to the future impl module -[[nodiscard]] constexpr Rational Rational::sum_or_subtract(const Rational& rhs, int sign) const { - if (_denominator == rhs.denominator()) { // Like fractions - return {static_cast(_numerator) + sign * static_cast(rhs.numerator()), - static_cast(_denominator) - }; - } else { // Unlike fractions - const int lhs_numerator = static_cast(_numerator); - const int rhs_numerator = sign * static_cast(rhs._numerator); - const int lhs_denominator = static_cast(_denominator); - const int rhs_denominator = static_cast(rhs._denominator); - - // Get their lcd by finding their lcm - const auto lcd = zero::math::lcm(_denominator.number(), rhs.denominator().number()); - - // Scale numerators to have the common denominator (lcm) - const int numerator = (lhs_numerator * (lcd / lhs_denominator)) + (rhs_numerator * (lcd / rhs_denominator)); - - return {numerator, lcd}; - } -} - -// Equality - -// TODO should we check that 4/2 is the same as 2/1 right? Or we should maintain the difference and explicitly -// say that 4/2 aren't the same Rational number as 2/1? -[[nodiscard]] inline constexpr bool Rational::operator==(const Rational rhs) const noexcept { - return _numerator == rhs.numerator() && _denominator == rhs.denominator(); -} diff --git a/zero/ifc/math/numbers/detail.cpp b/zero/ifc/math/numbers/detail.cpp new file mode 100644 index 0000000..c316aa2 --- /dev/null +++ b/zero/ifc/math/numbers/detail.cpp @@ -0,0 +1,115 @@ +module math:numbers.detail; + +import std; +import math.ops; +import math.symbols; + +import :general; +import :numbers.naturals; +import :numbers.integers; +import :numbers.rationals; + +#if defined(__clang__) // TODO: clang seems to be unable to resolve templates + // declared on internal partitions, so they must be + // 'exported' (even tho the module itself isn't being + // exported (module :numbers.detail), so this macro + // provisionally normalize the access of the template + // Number(s) operations to the implementation details + // internal module partition +export namespace zero::math { +#elif defined(_MSC_VER) +using namespace zero::math; +#endif + +template +concept EitherRational = + std::is_same_v || std::is_same_v; + +// Helper to extract the inner value of a Number type or avoid the method +// call if it's a primitive (std::is_arithmetic_v). +template constexpr auto normalize(const T &value) noexcept { + if constexpr (std::is_arithmetic_v || std::is_same_v) + return value; // Return primitive arithmetic types and Rationals as-is + else + return value.number(); +} + +// Generalized arithmetic operation helper using lambdas +template +constexpr auto arithmetic_op(const L &lhs, const R &rhs, Op op) noexcept { + if constexpr (EitherRational) + return op(lhs, rhs); + else + return op(normalize(lhs), normalize(rhs)); +} + +/** @brief Helper function to sum or subtract two Rationals + * @details it should be placed **before** the `rational_add` and rational_subtract` functions for being + * compilable with MSVC, as it has stricter name lookup resolution rules + */ +[[nodiscard]] constexpr Rational +sum_or_subtract(const Rational &lhs, const Rational &rhs, + const ArithmeticOperation op) noexcept { + const int sign = op == ArithmeticOperation::Add ? 1 : -1; + + const int lhs_numerator = lhs.numerator().number(); + const int rhs_numerator = sign * rhs.numerator().number(); + const int lhs_denominator = lhs.denominator().number(); + const int rhs_denominator = rhs.denominator().number(); + + if (lhs_denominator == rhs_denominator) { // Like fractions + return {lhs_numerator + rhs_numerator, lhs_denominator}; + } else { // Unlike fractions + // Get their LCD by finding their LCM + const auto lcd = zero::math::lcm(lhs_denominator, rhs_denominator); + + // Scale numerators to have the common denominator (LCM) + const int numerator = (lhs_numerator * (lcd / lhs_denominator)) + + (rhs_numerator * (lcd / rhs_denominator)); + + return {numerator, lcd}; + } +} + +// Specialized addition and subtraction for Rational types +template +constexpr auto rational_add_or_subtract(const L &lhs, const R &rhs, Op op) noexcept { + if constexpr (std::is_same_v && std::is_same_v) + return sum_or_subtract(lhs, rhs, op); + else if constexpr (std::is_same_v) + return sum_or_subtract(lhs, Rational(rhs), op); + else if constexpr (std::is_same_v) + return sum_or_subtract(Rational(lhs), rhs, op); +} + +template +constexpr auto rational_multiplication(const L &lhs, const R &rhs) noexcept { + const Rational _lhs = Rational(lhs); + const Rational _rhs = Rational(rhs); + + return Rational(_lhs.numerator().number() * _rhs.numerator().number(), + _lhs.denominator().number() * _rhs.denominator().number()); +} + +template +constexpr auto rational_division(const L &lhs, const R &rhs) noexcept { + const Rational _lhs = Rational(lhs); + const Rational _rhs = Rational(rhs); + + return Rational(_lhs.numerator().number() * _rhs.denominator().number(), + _lhs.denominator().number() * _rhs.numerator().number()); +} + +// Equality check for Rational types +template +constexpr auto rational_equality(const L &lhs, const R &rhs) noexcept { + const Rational _lhs = Rational(lhs); + const Rational _rhs = Rational(rhs); + + return _lhs.numerator().number() == _rhs.numerator().number() && + _lhs.denominator().number() == _rhs.denominator().number(); +} + +#if defined(__clang__) +} +#endif diff --git a/zero/ifc/math/numbers/integers.cppm b/zero/ifc/math/numbers/integers.cppm new file mode 100644 index 0000000..bd2978f --- /dev/null +++ b/zero/ifc/math/numbers/integers.cppm @@ -0,0 +1,36 @@ +export module math:numbers.integers; + +import std; +import math.ops; +import math.symbols; + +import :general; +import :numbers.naturals; + +export namespace zero::math { + /// A whole (non decimal nor fraction) real number + class Integer { + private: + signed int _number; + public: + constexpr static MathSymbol symbol { MathSymbol::Integers }; + + // TODO: explicit templated constructor constrained by Numerical? + // TODO: document in the public API that any decimal number will be truncated + [[nodiscard]] constexpr explicit Integer(const int value) noexcept : _number(value) {} + [[nodiscard]] explicit Integer(const Natural value) noexcept + : _number(static_cast(value.number())) {} + + /// @return a {@link signed int}, which is the value stored in the type, being a whole number (integer) + [[nodiscard]] constexpr signed int number() const noexcept { return _number; } + + // Explicit conversion operators + [[nodiscard]] constexpr explicit operator int() const { return _number; } + // Printable + friend std::ostream& operator<<(std::ostream& os, const Integer& rhs) { + os << rhs._number; + return os; + } + }; + +} diff --git a/zero/ifc/math/numbers/naturals.cppm b/zero/ifc/math/numbers/naturals.cppm new file mode 100644 index 0000000..5e46fd2 --- /dev/null +++ b/zero/ifc/math/numbers/naturals.cppm @@ -0,0 +1,30 @@ +export module math:numbers.naturals; + +import std; +import math.symbols; + +import :general; + +export namespace zero::math { + /// A positive integer number + class Natural { + private: + // TODO: shouldn't be unsigned. we may decide what kind of thing we do + // with signedness + int _number; + public: + constexpr static MathSymbol symbol { MathSymbol::Naturals }; + + [[nodiscard]] constexpr explicit Natural(const int value) noexcept : _number(value) {} + + /// @returns an {@link unsigned int}, which is the value stored in the type, being only a positive integer number + [[nodiscard]] constexpr int number() const noexcept { return _number; } + + /// TODO: should we do something about the values < 1? + /// Maybe promote them to Integer or just throw? + /* [[nodiscard]] Natural Natural::operator-(const Natural rhs) const noexcept { + return Natural(_number - rhs.number()); + } */ + }; +} + diff --git a/zero/ifc/math/numbers/numbers.cppm b/zero/ifc/math/numbers/numbers.cppm new file mode 100644 index 0000000..2742b84 --- /dev/null +++ b/zero/ifc/math/numbers/numbers.cppm @@ -0,0 +1,83 @@ +/// This module provides strong types over the most common sets of numbers in +/// mathematics + +export module math:numbers; + +import std; +import math.symbols; + +export import :numbers.naturals; +export import :numbers.integers; +export import :numbers.rationals; +export import :numbers.reals; +export import :general; + +import :numbers.detail; + +export namespace zero::math { + +// TODO: on the rational operations between Rational and a non-rational, +// we can definitely make the impl simpler by promoting the non rational +// to Rational (standalone templated helper that casts both to rationals +// maybe without if constexpr branches) + +template +constexpr auto operator+(const L &lhs, const R &rhs) noexcept { + auto op = [](const auto &a, const auto &b) { + if constexpr (EitherRational) + return rational_add_or_subtract(a, b, ArithmeticOperation::Add); + else + return a + b; + }; + return arithmetic_op(lhs, rhs, op); +} + +template +constexpr auto operator-(const L &lhs, const R &rhs) noexcept { + auto op = [](const auto &a, const auto &b) { + if constexpr (EitherRational) + return rational_add_or_subtract(a, b, ArithmeticOperation::Subtract); + else + return a - b; + }; + return arithmetic_op(lhs, rhs, op); +} + +template +constexpr auto operator*(const L &lhs, const R &rhs) noexcept { + auto op = [](const auto &a, const auto &b) { + if constexpr (EitherRational) + return rational_multiplication(a, b); + else + return a * b; + }; + return arithmetic_op(lhs, rhs, op); +} + +template +constexpr auto operator/(const L &lhs, const R &rhs) noexcept { + auto op = [](const auto &a, const auto &b) { + if constexpr (EitherRational) + return rational_division(a, b); + else + return a / b; + }; + return arithmetic_op(lhs, rhs, op); +} + +template +constexpr bool operator==(const L &lhs, const R &rhs) noexcept { + auto op = [](const auto &a, const auto &b) { + if constexpr (EitherRational) + return rational_equality(a, b); + else + return a == b; + }; + return arithmetic_op(lhs, rhs, op); +} + +template std::ostream &operator<<(std::ostream &os, const N &n) { + os << n.number(); + return os; +} +} // namespace zero::math diff --git a/zero/ifc/math/numbers/rationals.cppm b/zero/ifc/math/numbers/rationals.cppm new file mode 100644 index 0000000..63d574e --- /dev/null +++ b/zero/ifc/math/numbers/rationals.cppm @@ -0,0 +1,78 @@ +export module math:numbers.rationals; + +import std; +import math.ops; +import math.symbols; + +import :general; +import :numbers.naturals; +import :numbers.integers; + +export namespace zero::math { + /// @brief A type that represents rational numbers of the form: ℚ = {a, b ∈ ℤ, b ≠ 0} + /// + /// The Rational class encapsulates a fraction with an integer numerator and denominator. + /// Rational numbers are represented as ratios of integers, where the numerator belongs to ℤ + /// (the set of integers) and the denominator belongs to ℤ excluding zero. + /// + /// @note The class uses the Integer type to represent both the numerator and denominator. + /// + /// Example usage: + /// @code + /// Rational r1(1, 2); // Represents the fraction 1/2 + /// Rational r2(3, -4); // Represents the fraction -3/4 + /// Rational r; // Forbidden. Default constructor is not defined. Compile time error. + /// @endcode + /// + /// @apiNote The rational type allows the construction of fractions which are undefined, like (x, 0) x/0, + /// where the denominator is equals to zero, or even 0/0, and the operations are not checked, which this will + /// lead directly to **undefined behaviour** + class Rational { + private: + Integer _numerator; ///< The numerator of the rational number, belonging to ℤ. + Integer _denominator; ///< The denominator of the rational number, belonging to ℤ, NOT excluding the zero. + public: + constexpr static MathSymbol symbol = MathSymbol::Rationals; + + template // TODO: don't think that's work to have universal references over const l-value references + [[nodiscard]] constexpr Rational(L&& numerator, R&& denominator) noexcept + : _numerator(static_cast(std::forward(numerator))), + _denominator(static_cast(std::forward(denominator))) {} + + template // TODO: don't think that's work to have universal references over const l-value references + [[nodiscard]] constexpr Rational(N&& numerator) noexcept + : _numerator(static_cast(std::forward(numerator))), + _denominator(1) {} + + /* template + [[nodiscard]] constexpr Rational(const L& numerator, const R& denominator) noexcept + : _numerator(static_cast(numerator)), _denominator(static_cast(denominator)) {} */ + + [[nodiscard]] constexpr Rational(const Rational& other) noexcept = default; + [[nodiscard]] constexpr Rational(Rational&& other) noexcept = default; + + /// @return a {@link Integer} with the value of the numerator for this rational + [[nodiscard]] constexpr Integer numerator() const noexcept { return _numerator; } + + /// @return a {@link Integer} with the value of the denominator for this rational + [[nodiscard]] constexpr Integer denominator() const noexcept { return _denominator; } + + // TODO: Add a method to reduce fractions + + // Comparison operator overloads + // TODO: should we check that 4/2 is the same as 2/1 right? Or we should maintain the difference and explicitly + // say that 4/2 aren't the same Rational number as 2/1? +// [[nodiscard]] bool operator==(const Rational rhs) const noexcept { +// // return _numerator == rhs.numerator() && _denominator == rhs.denominator(); +// return _numerator.number() == rhs.numerator().number() && _denominator.number() == rhs.denominator().number(); +// } + + // Printable + friend std::ostream &operator<<(std::ostream& os, const Rational& rhs) { + os << rhs._numerator; + os << MathSymbol::DivisionSlash; + os << rhs._denominator; + return os; + } + }; +} diff --git a/zero/ifc/math/numbers/reals.cppm b/zero/ifc/math/numbers/reals.cppm new file mode 100644 index 0000000..5255b7c --- /dev/null +++ b/zero/ifc/math/numbers/reals.cppm @@ -0,0 +1,27 @@ + +export module math:numbers.reals; + +import std; +import math.ops; +import math.symbols; + +import :general; +import :numbers.naturals; +import :numbers.integers; +import :numbers.rationals; + +export namespace zero::math { + + template + class Real { + private: + T _number; + public: + constexpr static MathSymbol symbol = MathSymbol::Reals; + + [[nodiscard]] constexpr Real(const T& value) noexcept : _number{value} {} + [[nodiscard]] constexpr Real(const Real& other) noexcept = default; + [[nodiscard]] constexpr Real(Real&& other) noexcept = default; + [[nodiscard]] constexpr T number() const noexcept { return _number; } + }; +} diff --git a/zero/ifc/math/ops/arithmetic.cppm b/zero/ifc/math/ops/arithmetic.cppm index 3f607ca..8712f65 100644 --- a/zero/ifc/math/ops/arithmetic.cppm +++ b/zero/ifc/math/ops/arithmetic.cppm @@ -2,15 +2,17 @@ export module math.ops:arithmetic; import std; -export namespace zero::math { - template - [[nodiscard]] constexpr auto value_retriever(T& val) { - if constexpr (std::is_pointer_v) - return *val; - else - return val; - } +template +[[nodiscard]] constexpr auto value_retriever(T& val) { + if constexpr (std::is_pointer_v) + return *val; + else + return val; +} +// TODO: templates must be T, U. Two types, and constrained via the Number concept +// that later will be renamed as: Numerical being NumberBase CRTP class renamed to Number +export namespace zero::math { template [[nodiscard]] constexpr auto add(const T& lhs, const T& rhs) -> std::remove_pointer_t { return value_retriever(lhs) + value_retriever(rhs); diff --git a/zero/ifc/math/symbols.cppm b/zero/ifc/math/symbols.cppm index ae92343..405b0d0 100644 --- a/zero/ifc/math/symbols.cppm +++ b/zero/ifc/math/symbols.cppm @@ -1,145 +1,310 @@ export module math.symbols; -enum class MathSymbol { +import std; + +export enum class MathSymbol { // Basic Math Operators - Plus = 0x002B, // + - Minus = 0x2212, // - - Multiply = 0x00D7, // × - Divide = 0x00F7, // ÷ + Plus, // + + Minus, // - + Multiply, // × + Divide, // ÷ // General - Implies = 0x21D2, // ⇒ - NotImplies = 0x21CF, // ⇏ - IfAndOnlyIf = 0x21CF, // ⇔ - Increment = 0x2206, // ∆ + Implies, // ⇒ + NotImplies, // ⇏ + IfAndOnlyIf, // ⇔ + Increment, // ∆ // Relation - Equals = 0x003D, // = - NotEquals = 0x2260, // ≠ - AlmostEqualsTo = 0x2248, // ≈ - PlusMinus = 0x00B1, // ± - LessThan = 0x003C, // < - GreaterThan = 0x003E, // > - LessThanOrEqual = 0x2264, // ≤ - GreaterThanOrEqual = 0x2265, // ≥ - ProportionalTo = 0x221D, // ∝ - ApproximatelyEqual = 0x2248, // ≈ + Equals, // = + NotEquals, // ≠ + AlmostEqualsTo, // ≈ + PlusMinus, // ± + LessThan, // < + GreaterThan, // > + LessThanOrEqual, // ≤ + GreaterThanOrEqual, // ≥ + ProportionalTo, // ∝ + ApproximatelyEqual, // ≈ // Geometry - Parallel = 0x2225, // ∥ - NotParallel = 0x2226, // ∦ + Parallel, // ∥ + NotParallel, // ∦ // Root Symbols - SquareRoot = 0x221A, // √ - CubeRoot = 0x221B, // ∛ - FourthRoot = 0x221C, // ∜ + SquareRoot, // √ + CubeRoot, // ∛ + FourthRoot, // ∜ // Summation and Integral - Summation = 0x2211, // ∑ - Integral = 0x222B, // ∫ - DoubleIntegral = 0x222C, // ∬ - TripleIntegral = 0x222D, // ∭ - ContourIntegral = 0x222E, // ∮ - SurfaceIntegral = 0x222F, // ∯ - VolumeIntegral = 0x2230, // ∰ + Summation, // ∑ + Integral, // ∫ + DoubleIntegral, // ∬ + TripleIntegral, // ∭ + ContourIntegral, // ∮ + SurfaceIntegral, // ∯ + VolumeIntegral, // ∰ // Miscellaneous - Tilde = 0x223C, // ∼ - RingOperator = 0x2218, // ∘ - SineWave = 0x223F, // ∿ + Tilde, // ∼ + RingOperator, // ∘ + SineWave, // ∿ // Number Sets - NaturalNumbers = 0x2115, // ℕ - Integers = 0x2124, // ℤ - Rationals = 0x211A, // ℚ - Reals = 0x211D, // ℝ - ComplexNumbers = 0x2102, // ℂ + Naturals, // ℕ + Integers, // ℤ + Rationals, // ℚ + Reals, // ℝ + Complex, // ℂ + ImaginaryPart, // ℑ + RealPart, // ℜ + Quaternions, // ℍ + Primes, // ℙ // Set Notation - OpenCurlyBrace2 = 0x007B, // { - CloseCurlyBrace2 = 0x007D, // } - Exists = 0x2203, // ∃ (Exists) - ForAll = 0x2200, // ∀ (For All) - ElementOf = 0x2208, // ∈ - NotElementOf = 0x2209, // ∉ - ContainsAsMember = 0x220B, // ∋ (As member) - NotContainsAsMember = 0x220C, // ∌ (As member) - Subset = 0x2282, // ⊂ - SubsetOrEqualTo = 0x2286, // ⊆ - NotASubset = 0x2284, // ⊄ - Superset = 0x2285, // ⊃ - SupersetOrEqualTo = 0x2287, // ⊇ - NotASuperset = 0x2285, // ⊅ - EmptySet = 0x2205, // ∅ - Therefore = 0x2234, // ∴ - Because = 0x2235, // ∵ - Intersection = 0x2229, // ∩ - Union = 0x222A, // ∪ - SuchThat = 0x2223, // ∣ - DivisionSlash = 0x2044, // ⁄ (Division Slash) - OpenSquareBrace = 0x005B, // [ - CloseSquareBrace = 0x005D, // ] + OpenCurlyBrace2, // { + CloseCurlyBrace2, // } + Exists, // ∃ (Exists) + ForAll, // ∀ (For All) + ElementOf, // ∈ + NotElementOf, // ∉ + ContainsAsMember, // ∋ (As member) + NotContainsAsMember, // ∌ (As member) + Subset, // ⊂ + SubsetOrEqualTo, // ⊆ + NotASubset, // ⊄ + Superset, // ⊃ + SupersetOrEqualTo, // ⊇ + NotASuperset, // ⊅ + EmptySet, // ∅ + Therefore, // ∴ + Because, // ∵ + Intersection, // ∩ + Union, // ∪ + SuchThat, // ∣ + DivisionSlash, // ⁄ (Division Slash) + OpenSquareBrace, // [ + CloseSquareBrace, // ] // Logical Operators - LogicalAnd = 0x2227, // ∧ - LogicalOr = 0x2228, // ∨ - LogicalNot = 0x00AC, // ¬ + LogicalAnd, // ∧ + LogicalOr, // ∨ + LogicalNot, // ¬ // Infinity and Special Symbols - Infinity = 0x221E, // ∞ - MinusInfinity = 0x2212, // -∞ + Infinity, // ∞ + MinusInfinity, // -∞ // Aleph and Parentheses - Aleph = 0x2135, // ℵ - OpenParenthesis = 0x0028, // ( - CloseParenthesis = 0x0029, // ) + Aleph, // ℵ + OpenParenthesis, // ( + CloseParenthesis, // ) // Superscript and Subscript - SuperscriptN = 0x207F, // ⁿ (Superscript n) - Superscript1 = 0x00B9, // ¹ (Superscript 1) - Superscript2 = 0x00B2, // ² (Superscript 2) - Superscript3 = 0x00B3, // ³ (Superscript 3) - SuperscriptPlus = 0x207A, // ⁺ (Superscript Plus) - SuperscriptMinus = 0x207B, // ⁻ (Superscript Minus) - Subscript1 = 0x2081, // ₁ (Subscript 1) - Subscript2 = 0x2082, // ₂ (Subscript 2) - Subscript3 = 0x2083, // ₃ (Subscript 3) + SuperscriptN, // ⁿ (Superscript n) + Superscript1, // ¹ (Superscript 1) + Superscript2, // ² (Superscript 2) + Superscript3, // ³ (Superscript 3) + SuperscriptPlus, // ⁺ (Superscript Plus) + SuperscriptMinus, // ⁻ (Superscript Minus) + Subscript1, // ₁ (Subscript 1) + Subscript2, // ₂ (Subscript 2) + Subscript3, // ₃ (Subscript 3) // Derivative Symbols - Derivative = 0x2146, // ⅆ (Derivative of) - PartialDerivative = 0x2202, // ∂ (Partial Derivative) - Nabla = 0x2207, // ∇ (Nabla) - DelSquared = 0x2207, // ∇² (Del Squared) - VectorDiv = 0x2207, // ∇ · (Divergence) - Laplace = 0x2207, // ∇² (Laplace Operator) + Derivative, // ⅆ (Derivative of) + PartialDerivative, // ∂ (Partial Derivative) + Nabla, // ∇ (Nabla) + DelSquared, // ∇² (Del Squared) + VectorDiv, // ∇ · (Divergence) + Laplace, // ∇² (Laplace Operator) // Vector Symbols - VectorArrow = 0x2192, // → (Vector Arrow) - CrossProduct = 0x00D7, // × (Cross Product) - DotProduct = 0x22C5, // ⋅ (Dot Product) + VectorArrow, // → (Vector Arrow) + CrossProduct, // × (Cross Product) + DotProduct, // ⋅ (Dot Product) // Matrices Symbols // TODO complete - Matrix = 0x23A0, // ⎠ (Matrix) - MatrixTranspose = 0x22A4, // ⊤ (Matrix Transpose) - MatrixHermitian = 0x22B2, // ⊲ (Matrix Hermitian) - - // Degree and Greek Letters - Degree = 0x00B0, // ° - Pi = 0x03C0, // π - Sigma = 0x03A3, // Σ - Delta = 0x2206, // ∆ - Alpha = 0x03B1, // α - Beta = 0x03B2, // β - Gamma = 0x03B3, // γ - Epsilon = 0x03B5, // ε - Zeta = 0x03B6, // ζ - Eta = 0x03B7, // η - Mu = 0x03BC, // μ - Nu = 0x03BD, // ν - Xi = 0x039E, // Ξ - Rho = 0x03C1, // ρ - Tau = 0x03C4, // τ - Phi = 0x03A6, // Φ - Psi = 0x03A8, // Ψ - Omega = 0x03A9, // Ω + Matrix, // ⎠ (Matrix) + MatrixTranspose, // ⊤ (Matrix Transpose) + MatrixHermitian, // ⊲ (Matrix Hermitian) + + // Like Letters + Degree, // ° + EulerNumber, // ℯ + Pi, // π + Sigma, // Σ + Delta, // ∆ + Alpha, // α + Beta, // β + Gamma, // γ + Epsilon, // ε + Zeta, // ζ + Eta, // η + Mu, // μ + Nu, // ν + Xi, // Ξ + Rho, // ρ + Tau, // τ + Phi, // Φ + Psi, // Ψ + Omega // Ω }; + +constexpr const char* to_string(const MathSymbol symbol) { + switch (symbol) { + // Basic Math Operators + case MathSymbol::Plus: return "+"; + case MathSymbol::Minus: return "-"; + case MathSymbol::Multiply: return "×"; + case MathSymbol::Divide: return "÷"; + + // General + case MathSymbol::Implies: return "⇒"; + case MathSymbol::NotImplies: return "⇏"; + case MathSymbol::IfAndOnlyIf: return "⇔"; + case MathSymbol::Increment: return "∆"; + + // Relation + case MathSymbol::Equals: return "="; + case MathSymbol::NotEquals: return "≠"; + case MathSymbol::AlmostEqualsTo: return "≈"; + case MathSymbol::PlusMinus: return "±"; + case MathSymbol::LessThan: return "<"; + case MathSymbol::GreaterThan: return ">"; + case MathSymbol::LessThanOrEqual: return "≤"; + case MathSymbol::GreaterThanOrEqual: return "≥"; + case MathSymbol::ProportionalTo: return "∝"; + case MathSymbol::ApproximatelyEqual: return "≈"; + + // Geometry + case MathSymbol::Parallel: return "∥"; + case MathSymbol::NotParallel: return "∦"; + + // Root Symbols + case MathSymbol::SquareRoot: return "√"; + case MathSymbol::CubeRoot: return "∛"; + case MathSymbol::FourthRoot: return "∜"; + + // Summation and Integral + case MathSymbol::Summation: return "∑"; + case MathSymbol::Integral: return "∫"; + case MathSymbol::DoubleIntegral: return "∬"; + case MathSymbol::TripleIntegral: return "∭"; + case MathSymbol::ContourIntegral: return "∮"; + case MathSymbol::SurfaceIntegral: return "∯"; + case MathSymbol::VolumeIntegral: return "∰"; + + // Miscellaneous + case MathSymbol::Tilde: return "∼"; + case MathSymbol::RingOperator: return "∘"; + case MathSymbol::SineWave: return "∿"; + + // Number Sets + case MathSymbol::Naturals: return "ℕ"; + case MathSymbol::Integers: return "ℤ"; + case MathSymbol::Rationals: return "ℚ"; + case MathSymbol::Reals: return "ℝ"; + case MathSymbol::Complex: return "ℂ"; + case MathSymbol::ImaginaryPart: return "ℑ"; + case MathSymbol::RealPart: return "ℜ"; + case MathSymbol::Quaternions: return "ℍ"; + case MathSymbol::Primes: return "ℙ"; + + // Set Notation + case MathSymbol::OpenCurlyBrace2: return "{"; + case MathSymbol::CloseCurlyBrace2: return "}"; + case MathSymbol::Exists: return "∃"; + case MathSymbol::ForAll: return "∀"; + case MathSymbol::ElementOf: return "∈"; + case MathSymbol::NotElementOf: return "∉"; + case MathSymbol::ContainsAsMember: return "∋"; + case MathSymbol::NotContainsAsMember: return "∌"; + case MathSymbol::Subset: return "⊂"; + case MathSymbol::SubsetOrEqualTo: return "⊆"; + case MathSymbol::NotASubset: return "⊄"; + case MathSymbol::Superset: return "⊃"; + case MathSymbol::SupersetOrEqualTo: return "⊇"; + case MathSymbol::NotASuperset: return "⊅"; + case MathSymbol::EmptySet: return "∅"; + case MathSymbol::Therefore: return "∴"; + case MathSymbol::Because: return "∵"; + case MathSymbol::Intersection: return "∩"; + case MathSymbol::Union: return "∪"; + case MathSymbol::SuchThat: return "∣"; + case MathSymbol::DivisionSlash: return "⁄"; + case MathSymbol::OpenSquareBrace: return "["; + case MathSymbol::CloseSquareBrace: return "]"; + + // Logical Operators + case MathSymbol::LogicalAnd: return "∧"; + case MathSymbol::LogicalOr: return "∨"; + case MathSymbol::LogicalNot: return "¬"; + + // Infinity and Special Symbols + case MathSymbol::Infinity: return "∞"; + case MathSymbol::MinusInfinity: return "-∞"; + + // Aleph and Parentheses + case MathSymbol::Aleph: return "ℵ"; + case MathSymbol::OpenParenthesis: return "("; + case MathSymbol::CloseParenthesis: return ")"; + + // Superscript and Subscript + case MathSymbol::SuperscriptN: return "ⁿ"; + case MathSymbol::Superscript1: return "¹"; + case MathSymbol::Superscript2: return "²"; + case MathSymbol::Superscript3: return "³"; + case MathSymbol::SuperscriptPlus: return "⁺"; + case MathSymbol::SuperscriptMinus: return "⁻"; + case MathSymbol::Subscript1: return "₁"; + case MathSymbol::Subscript2: return "₂"; + case MathSymbol::Subscript3: return "₃"; + + // Derivative Symbols + case MathSymbol::Derivative: return "ⅆ"; + case MathSymbol::PartialDerivative: return "∂"; + case MathSymbol::Nabla: return "∇"; + case MathSymbol::DelSquared: return "∇²"; + case MathSymbol::VectorDiv: return "∇ ·"; + case MathSymbol::Laplace: return "∇²"; + + // Vector Symbols + case MathSymbol::VectorArrow: return "→"; + case MathSymbol::CrossProduct: return "×"; + case MathSymbol::DotProduct: return "⋅"; + + // Matrices Symbols // TODO complete + case MathSymbol::Matrix: return "⎠"; + case MathSymbol::MatrixTranspose: return "⊤"; + case MathSymbol::MatrixHermitian: return "⊲"; + + // Like Letters + case MathSymbol::Degree: return "°"; + case MathSymbol::EulerNumber: return "ℯ"; + case MathSymbol::Pi: return "π"; + case MathSymbol::Sigma: return "Σ"; + case MathSymbol::Delta: return "∆"; + case MathSymbol::Alpha: return "α"; + case MathSymbol::Beta: return "β"; + case MathSymbol::Gamma: return "γ"; + case MathSymbol::Epsilon: return "ε"; + case MathSymbol::Zeta: return "ζ"; + case MathSymbol::Eta: return "η"; + case MathSymbol::Mu: return "μ"; + case MathSymbol::Nu: return "ν"; + case MathSymbol::Xi: return "Ξ"; + case MathSymbol::Rho: return "ρ"; + case MathSymbol::Tau: return "τ"; + case MathSymbol::Phi: return "Φ"; + case MathSymbol::Psi: return "Ψ"; + case MathSymbol::Omega: return "Ω"; + } + + return "Unknown"; +} + +export inline std::ostream& operator<<(std::ostream& os, const MathSymbol& symbol) { + os << to_string(symbol); + return os; +} \ No newline at end of file diff --git a/zero/ifc/physics/quantities/internal/quantities_detail.cpp b/zero/ifc/physics/quantities/internal/quantities_detail.cpp index 2de21f5..0165702 100644 --- a/zero/ifc/physics/quantities/internal/quantities_detail.cpp +++ b/zero/ifc/physics/quantities/internal/quantities_detail.cpp @@ -2,11 +2,6 @@ * @brief Partition for holding the implementation * details that won't contribute to the external API of the * units module. - * - * Due to the recursive function template for - * formatting the `derived magnitude symbols`, we can't use - * this as an internal partition, due to the linker is not able - * to resolve the template in order to link it at call site */ export module physics.quantities:quantities.detail; @@ -16,7 +11,7 @@ import std; import type_info; import str_manip; -export namespace quantities::__detail { +namespace quantities::__detail { template struct dimensions_exponents; @@ -63,4 +58,4 @@ export namespace quantities::__detail { if constexpr (N - 1 < total_elements - 1) derived_magnitude_symbols(out); } -} \ No newline at end of file +} diff --git a/zero/ifc/physics/quantities/units.cppm b/zero/ifc/physics/quantities/units.cppm index 76c405e..e2ef129 100644 --- a/zero/ifc/physics/quantities/units.cppm +++ b/zero/ifc/physics/quantities/units.cppm @@ -19,7 +19,7 @@ using namespace zero::math; export namespace zero::physics { /* Base units */ - template +template struct base_unit { using ratio = r; using symbol = s; diff --git a/zero/ifc/test-suite/assertions.cppm b/zero/ifc/test-suite/assertions.cppm index 916c522..eddf018 100644 --- a/zero/ifc/test-suite/assertions.cppm +++ b/zero/ifc/test-suite/assertions.cppm @@ -1,57 +1,109 @@ export module tsuite:assertions; import std; +import concepts; + +using namespace zero::concepts; + +template +constexpr inline void throw_on_failed_test(const T& expected, const U& actual, const Op& comp); export { - /** - * Compares two values. Generates a test failed if values are non-equal. - */ - template - requires (!std::is_pointer_v) - void assertEquals(const T& expected, const T& actual) { - if (expected != actual) - throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) + - ", actual = " + std::to_string(actual)); + /// @brief Compares two values. Generates a test failed if values are non-equal. + /// @param actual the given or computed value + /// @param expected the given expectation for the computed value + template + requires (!std::is_pointer_v && !std::is_pointer_v) + constexpr void assertEquals(const T& expected, const U& actual) { + ::throw_on_failed_test(expected, actual, [](auto expected, auto actual){ return expected == actual; }); } - /** - * Compares two values being T pointer types. - * Generates a test failed if the values after dereference - * the pointers are non-equal. - */ + /// @brief Compares two values being T pointer types. + /// Generates a test failed if the values after dereference the pointers are non-equal. template - void assertEquals(const T* expected_ptr, const T* actual_ptr) { - auto expected = *expected_ptr; - auto actual = *actual_ptr; + constexpr void assertEquals(const T* expected_ptr, const T* actual_ptr) { + const auto expected = *expected_ptr; + const auto actual = *actual_ptr; - if (expected != actual) - throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) + - ", actual = " + std::to_string(actual)); + ::throw_on_failed_test(expected, actual, [](auto expected, auto actual){ return expected == actual; }); } - /** - * Compares two values. Generates a test failed if the values are equals. - */ + + /// @brief Compares two values. Generates a test failed if the values are equals. template requires (!std::is_pointer_v) - void assertNotEquals(const T& expected, const T& actual) { - if (expected == actual) - throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) + - ", actual = " + std::to_string(actual)); + constexpr void assertNotEquals(const T& expected, const T& actual) { + ::throw_on_failed_test(expected, actual, [](auto expected, auto actual){ return expected != actual; }); } - /** - * Compares two values being T pointer types. - * Generates a test failed if the values after dereference - * the pointers are non-equal. - */ + + /// @brief Compares two values being T pointer types. + /// Generates a test failed if the values after dereference + /// the pointers are non-equal. template - void assertNotEquals(const T* expected_ptr, const T* actual_ptr) { - auto expected = *expected_ptr; - auto actual = *actual_ptr; + constexpr void assertNotEquals(const T* expected_ptr, const T* actual_ptr) { + const auto expected = *expected_ptr; + const auto actual = *actual_ptr; + + ::throw_on_failed_test(expected, actual, [](auto expected, auto actual){ return expected != actual; }); + } +} + +// Detail -- TODO move to an impl module partition + +/// \brief custom internal exception for the assertions failure +class assertion_failed : public std::exception { +private: + std::string message_; - if (expected == actual) - throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) + - ", actual = " + std::to_string(actual)); +public: + explicit assertion_failed(const std::string& message) + : message_(message) {} + + [[nodiscard]] const char* what() const noexcept override { + return message_.c_str(); + } +}; + +/// \brief helper to reduce cognitive complexity, +/// \enabled when the concept {\link @StringConvertible} is satisfied +template +constexpr inline void throw_on_failed_test_str_impl(const T& expected, const U& actual, const Op& comp) { + if (!comp(expected, actual)) { + const auto expected_str = std::to_string(expected); + const auto actual_str = std::to_string(actual); + + std::string msg {}; + msg.reserve(29 + expected_str.size() + actual_str.size() + 11); + + msg += "Assertion failed: expected = "; // 29 + msg += expected_str; + msg += ", actual = "; // 11 + msg += actual_str; + + throw assertion_failed(msg); } -} \ No newline at end of file +} + +/// \brief helper to reduce cognitive complexity, +/// \enabled when the concept {\link @Ostreamable} is satisfied +template +constexpr inline void throw_on_failed_test_oss_impl(const T& expected, const U& actual, const Op& comp) { + if (!comp(expected, actual)) { + std::ostringstream oss; + oss << "Assertion failed: expected = "; + oss << expected; + oss << ", actual = "; + oss << actual; + + throw assertion_failed(oss.str()); // TODO use a better one + } +} + +template +constexpr inline void throw_on_failed_test(const T& expected, const U& actual, const Op& comp) { + if constexpr (StringConvertible && StringConvertible) + throw_on_failed_test_str_impl(expected, actual, comp); + else + throw_on_failed_test_oss_impl(expected, actual, comp); +} diff --git a/zero/ifc/test-suite/suite.cppm b/zero/ifc/test-suite/suite.cppm index 57f79e0..ddcb744 100644 --- a/zero/ifc/test-suite/suite.cppm +++ b/zero/ifc/test-suite/suite.cppm @@ -16,7 +16,7 @@ import stylizer; import formatter; import print_utils; -using namespace zero::fmt; +using namespace zero; /** * @struct TestResults @@ -161,6 +161,11 @@ void runSuiteTestCases(const TestRunBehavior behavior); */ void checkForTestErrors(const bool freeTestsErrors); +/* + * Litte helper for a visual separator tab + */ +void print_separator(const fmt::Color color = fmt::Color::EXT_SKY_BLUE); + // Top-level containers. They hold pointers to the types to avoid: // `arithmetic on a pointer to an incomplete type` std::vector testSuites; @@ -243,17 +248,19 @@ export { tsuite.cases.emplace_back(new TestCase(tname, tfunc)); else tsuite.results.warnings.emplace_back( - stylize("[Warning in suite: ", Color::YELLOW, {}) + - stylize(tsuite.uuid, Color::EXT_PURPLE, {}) + - stylize("] Already exists a test case with the name: ", - Color::YELLOW, {}) + - stylize(tname, Color::EXT_SKY_BLUE, {}) + - stylize(". Skipping test case.", Color::YELLOW, {})); - /// If this is the first time that the suite is being registered + fmt::stylize("[Warning in suite: ", fmt::Color::YELLOW) + + fmt::stylize(tsuite.uuid, fmt::Color::EXT_PURPLE) + + fmt::stylize("] Already exists a test case with the name: ", fmt::Color::YELLOW) + + fmt::stylize(tname, fmt::Color::EXT_SKY_BLUE) + + fmt::stylize(". Skipping test case.", fmt::Color::YELLOW) + ); + + /// If this is the first time that the suite is being registered auto suites_it = std::find_if( testSuites.begin(), testSuites.end(), [&](const TestSuite *suite) { return suite->uuid == tsuite.uuid; }); - if (suites_it == testSuites.end()) + + if (suites_it == testSuites.end()) testSuites.push_back(&tsuite); } @@ -270,62 +277,67 @@ export { } void runSuiteTestCases(const TestRunBehavior behavior) { - println("\nRunning test suites. Total suites found: {}", testSuites.size()); + fmt::println( + fmt::stylize("\n============================= ZERO TEST SUITE =============================", fmt::Color::GREEN, fmt::Modifier::BOLD) + ); + fmt::println("- Running test suites. Total suites found: {}", testSuites.size()); + print_separator(fmt::Color::GREEN); + fmt::newln(); for (const auto &test_suite : testSuites) { - print("Running test suite:" + stylize(" {}", Color::EXT_PURPLE, {}), - test_suite->uuid); + print_separator(); + fmt::println("- Running test suite:" + fmt::stylize(" {}", fmt::Color::EXT_PURPLE), test_suite->uuid); for (const auto &warning : test_suite->results.warnings) - print("\n {}", warning); + fmt::println(" {}", warning); for (const auto &test_case : test_suite->cases) { if (!runTest(test_case, test_suite->results)) { if (behavior == HALT_SUITE_ON_FAIL) { - println( - stylize("\n========================================" + fmt::println( + fmt::stylize("\n========================================" "\n[Halt Suite Tests] Stopping further tests " "of the suite ", - Color::EXT_LIGHT_ORANGE, {Modifier::BOLD}) + - stylize("{} ", Color::EXT_PURPLE, {}) + - stylize( + fmt::Color::EXT_LIGHT_ORANGE, {fmt::Modifier::BOLD}) + + fmt::stylize("{} ", fmt::Color::EXT_PURPLE) + + fmt::stylize( "due to a failure." "\n========================================", - Color::EXT_LIGHT_ORANGE, {Modifier::BOLD}), + fmt::Color::EXT_LIGHT_ORANGE, {fmt::Modifier::BOLD}), test_suite->uuid); break; } if (behavior == ABORT_ALL_ON_FAIL) { - println("Test suite [{}] summary:", test_suite->uuid); - println(stylize(" Passed: {}", Color::GREEN, {}), + fmt::println("Test suite [{}] summary:", test_suite->uuid); + fmt::println(fmt::stylize(" Passed: {}", fmt::Color::GREEN), test_suite->results.passed); - println(stylize(" Failed: {}", Color::RED, {}), + fmt::println(fmt::stylize(" Failed: {}", fmt::Color::RED), test_suite->results.failed); - println( - stylize("\n========================================" + fmt::println( + fmt::stylize("\n========================================" "\n[Abort] All further tests are aborted due " "to a failure in a test in this suite." "\n========================================", - Color::RED, {Modifier::BOLD})); + fmt::Color::RED, fmt::Modifier::BOLD)); return; } } } - println("Test suite [{}] summary:", test_suite->uuid); - println(stylize(" Passed: {}", Color::GREEN, {}), - test_suite->results.passed); - println(stylize(" Failed: {}", Color::RED, {}), - test_suite->results.failed); + fmt::println("- Test suite [{}] summary:", test_suite->uuid); + fmt::println(fmt::stylize("\tPassed: {}", fmt::Color::GREEN), test_suite->results.passed); + fmt::println(fmt::stylize("\tFailed: {}", fmt::Color::RED), test_suite->results.failed); // TODO: + // Add the names of the FAILED test cases? + print_separator(); } } bool runFreeTestCases(const TestRunBehavior behavior) { bool anyFailed = false; TestResults freeTestsResults; - println("Running free tests:"); + fmt::println("Running free tests:"); for (const auto &testCase : freeTestCases) { if (!runTest(testCase, freeTestsResults)) { @@ -337,24 +349,24 @@ bool runFreeTestCases(const TestRunBehavior behavior) { } } - println("\nFree tests summary:"); - println(stylize(" Passed: {}", Color::GREEN, {}), + fmt::println("\nFree tests summary:"); + fmt::println(fmt::stylize(" Passed: {}", fmt::Color::GREEN), freeTestsResults.passed); - println(stylize(" Failed: {}", Color::RED, {}), freeTestsResults.failed); + fmt::println(fmt::stylize(" Failed: {}", fmt::Color::RED), freeTestsResults.failed); if (anyFailed) { if (behavior == HALT_SUITE_ON_FAIL) { - println(stylize("\n========================================" + fmt::println(fmt::stylize("\n========================================" "\n[Halt Free Tests] Stopping further free tests " "due to a failure." "\n========================================", - Color::EXT_LIGHT_ORANGE, {Modifier::BOLD})); + fmt::Color::EXT_LIGHT_ORANGE, {fmt::Modifier::BOLD})); } else if (behavior == ABORT_ALL_ON_FAIL) { - println(stylize("\n========================================" + fmt::println(fmt::stylize("\n========================================" "\n[Abort] All further tests are aborted due to a " "failure in free tests." "\n========================================", - Color::RED, {Modifier::BOLD})); + fmt::Color::RED, {fmt::Modifier::BOLD})); std::exit(1); } } @@ -363,17 +375,15 @@ bool runFreeTestCases(const TestRunBehavior behavior) { } bool runTest(const TestCase *const testCase, TestResults &results) { - print("\n Running test: {}", - stylize(testCase->name, Color::EXT_SKY_BLUE, {})); + fmt::print(" [[Test]]: {}", fmt::stylize(testCase->name, fmt::Color::EXT_SKY_BLUE)); try { // Call the test function testCase->fn(); - print(" ... Result => {}", stylize("Passed!", Color::GREEN, {})); + fmt::println(" ... => {}", fmt::stylize("Passed!", fmt::Color::GREEN)); results.passed++; return true; } catch (const std::exception &ex) { - print(" ... Result => {}: {}", stylize("Failed", Color::RED, {}), - ex.what()); + fmt::println(" ... => {}:\n\t{}", fmt::stylize("Failed", fmt::Color::RED), ex.what()); results.failed++; return false; } @@ -387,4 +397,10 @@ void checkForTestErrors(const bool freeTestsErrors) { std::cout << freeTestsErrors << " " << suiteTestsErrors; if (suiteTestsErrors || freeTestsErrors) std::exit(1); -} \ No newline at end of file +} + +void print_separator(const fmt::Color color) { + fmt::println( + fmt::stylize("============================================================================", color, {fmt::Modifier::BOLD}) + ); +} diff --git a/zero/ifc/text/formatter.cppm b/zero/ifc/text/formatter.cppm index c67c008..8eecc5b 100644 --- a/zero/ifc/text/formatter.cppm +++ b/zero/ifc/text/formatter.cppm @@ -21,7 +21,7 @@ export namespace zero::fmt { std::string result; bool escape_next = false; - for (size_t i = 0; i < format.size(); ++i) { + for (std::size_t i = 0; i < format.size(); ++i) { if (format[i] == '\\' && !escape_next) { escape_next = true; } else if (format[i] == '{' && i + 1 < format.size() && format[i + 1] == '}' && !escape_next) { diff --git a/zero/ifc/text/print_utils.cppm b/zero/ifc/text/print_utils.cppm index d81438f..a9fc7f7 100644 --- a/zero/ifc/text/print_utils.cppm +++ b/zero/ifc/text/print_utils.cppm @@ -1,10 +1,9 @@ export module print_utils; -import formatter; import std; +import formatter; export namespace zero::fmt { - template constexpr void print(const std::string& format, Args... args) { std::cout << formatter(format, args...); diff --git a/zero/ifc/text/stylizer.cppm b/zero/ifc/text/stylizer.cppm index 334d18b..0f48980 100644 --- a/zero/ifc/text/stylizer.cppm +++ b/zero/ifc/text/stylizer.cppm @@ -8,144 +8,165 @@ import std; export namespace zero::fmt { -enum class Color { - DEFAULT, // Default console color - - // Standard ANSI foreground colors - BLACK = 30, - RED = 31, - GREEN = 32, - YELLOW = 33, - BLUE = 34, - MAGENTA = 35, - CYAN = 36, - WHITE = 37, - - // Bright ANSI foreground colors - BRIGHT_BLACK = 90, - BRIGHT_RED = 91, - BRIGHT_GREEN = 92, - BRIGHT_YELLOW = 93, - BRIGHT_BLUE = 94, - BRIGHT_MAGENTA = 95, - BRIGHT_CYAN = 96, - BRIGHT_WHITE = 97, - - // Standard ANSI background colors - BG_BLACK = 40, - BG_RED = 41, - BG_GREEN = 42, - BG_YELLOW = 43, - BG_BLUE = 44, - BG_MAGENTA = 45, - BG_CYAN = 46, - BG_WHITE = 47, - - // Bright ANSI background colors - BG_BRIGHT_BLACK = 100, - BG_BRIGHT_RED = 101, - BG_BRIGHT_GREEN = 102, - BG_BRIGHT_YELLOW = 103, - BG_BRIGHT_BLUE = 104, - BG_BRIGHT_MAGENTA = 105, - BG_BRIGHT_CYAN = 106, - BG_BRIGHT_WHITE = 107, - - // Extended 256-color palette - // Reds - EXT_RED = 196, - EXT_LIGHT_RED = 203, - EXT_DARK_RED = 124, - EXT_CRIMSON_RED = 160, - EXT_FIREBRICK_RED = 88, - EXT_CORAL_RED = 210, - EXT_INDIAN_RED = 167, - EXT_SALMON_RED = 209, - - // Oranges - EXT_ORANGE = 202, - EXT_LIGHT_ORANGE = 214, - EXT_DARK_ORANGE = 208, - EXT_SOFT_ORANGE = 216, - EXT_DEEP_ORANGE = 166, - - // Yellows - EXT_YELLOW = 226, - EXT_LIGHT_YELLOW = 228, - EXT_DARK_YELLOW = 220, - EXT_GOLDEN_YELLOW = 184, - EXT_SUN_YELLOW = 190, - EXT_MUSTARD_YELLOW = 172, - - // Greens - EXT_GREEN = 46, - EXT_LIGHT_GREEN = 82, - EXT_DARK_GREEN = 28, - EXT_LIME_GREEN = 118, - EXT_SEA_GREEN = 85, - EXT_OLIVE_GREEN = 58, - EXT_EMERALD_GREEN = 34, - - // Blues - EXT_BLUE = 21, - EXT_LIGHT_BLUE = 39, - EXT_DARK_BLUE = 19, - EXT_SKY_BLUE = 117, - EXT_NAVY_BLUE = 17, - EXT_AQUA_BLUE = 45, - EXT_TEAL_BLUE = 30, - EXT_STEEL_BLUE = 67, - - // Purples - EXT_PURPLE = 93, - - // Other colors - EXT_CYAN = 51, - EXT_WHITE = 15, - EXT_GREY = 244, - EXT_BLACK = 16, -}; - -enum class Modifier { - BOLD = 1, - FAINT = 2, - ITALIC = 3, - UNDERLINE = 4, - BLINK = 5, - REVERSE = 7, - HIDDEN = 8 -}; - -std::string stylize(const std::string &text, Color color, - const std::vector &modifiers) { - // The color code will depend on whether we are using a standard ANSI color - // or an extended color from the 256-color palette. - std::string colorCode = ""; - if (static_cast(color) >= 30 && static_cast(color) <= 37) { - // Standard ANSI colors are within the range of 30-37. - // These are directly used following the escape sequence "\033[". - colorCode = "\033[" + std::to_string(static_cast(color)) + "m"; - } else { - // Colors outside the range of standard ANSI colors are treated as part - // of the extended 256-color palette. They require a prefix "38;5;" - // which signals the terminal to interpret the following number as a - // 256-palette index. - colorCode = - "\033[38;5;" + std::to_string(static_cast(color)) + "m"; - } - - std::string modifierCodes = ""; - - for (const auto &modifier : modifiers) { - modifierCodes += - "\033[" + std::to_string(static_cast(modifier)) + "m"; - } - - std::string resetCodes = ""; - for (size_t i = 0; i < modifiers.size(); ++i) { - resetCodes += "\033[0m"; - } - - return modifierCodes + colorCode + text + resetCodes + "\033[0m"; + enum class Color { + DEFAULT, // Default console color + + // Standard ANSI foreground colors + BLACK = 30, + RED = 31, + GREEN = 32, + YELLOW = 33, + BLUE = 34, + MAGENTA = 35, + CYAN = 36, + WHITE = 37, + + // Bright ANSI foreground colors + BRIGHT_BLACK = 90, + BRIGHT_RED = 91, + BRIGHT_GREEN = 92, + BRIGHT_YELLOW = 93, + BRIGHT_BLUE = 94, + BRIGHT_MAGENTA = 95, + BRIGHT_CYAN = 96, + BRIGHT_WHITE = 97, + + // Standard ANSI background colors + BG_BLACK = 40, + BG_RED = 41, + BG_GREEN = 42, + BG_YELLOW = 43, + BG_BLUE = 44, + BG_MAGENTA = 45, + BG_CYAN = 46, + BG_WHITE = 47, + + // Bright ANSI background colors + BG_BRIGHT_BLACK = 100, + BG_BRIGHT_RED = 101, + BG_BRIGHT_GREEN = 102, + BG_BRIGHT_YELLOW = 103, + BG_BRIGHT_BLUE = 104, + BG_BRIGHT_MAGENTA = 105, + BG_BRIGHT_CYAN = 106, + BG_BRIGHT_WHITE = 107, + + // Extended 256-color palette + // Reds + EXT_RED = 196, + EXT_LIGHT_RED = 203, + EXT_DARK_RED = 124, + EXT_CRIMSON_RED = 160, + EXT_FIREBRICK_RED = 88, + EXT_CORAL_RED = 210, + EXT_INDIAN_RED = 167, + EXT_SALMON_RED = 209, + + // Oranges + EXT_ORANGE = 202, + EXT_LIGHT_ORANGE = 214, + EXT_DARK_ORANGE = 208, + EXT_SOFT_ORANGE = 216, + EXT_DEEP_ORANGE = 166, + + // Yellows + EXT_YELLOW = 226, + EXT_LIGHT_YELLOW = 228, + EXT_DARK_YELLOW = 220, + EXT_GOLDEN_YELLOW = 184, + EXT_SUN_YELLOW = 190, + EXT_MUSTARD_YELLOW = 172, + + // Greens + EXT_GREEN = 46, + EXT_LIGHT_GREEN = 82, + EXT_DARK_GREEN = 28, + EXT_LIME_GREEN = 118, + EXT_SEA_GREEN = 85, + EXT_OLIVE_GREEN = 58, + EXT_EMERALD_GREEN = 34, + + // Blues + EXT_BLUE = 21, + EXT_LIGHT_BLUE = 39, + EXT_DARK_BLUE = 19, + EXT_SKY_BLUE = 117, + EXT_NAVY_BLUE = 17, + EXT_AQUA_BLUE = 45, + EXT_TEAL_BLUE = 30, + EXT_STEEL_BLUE = 67, + + // Purples + EXT_PURPLE = 93, + + // Other colors + EXT_CYAN = 51, + EXT_WHITE = 15, + EXT_GREY = 244, + EXT_BLACK = 16, + }; + + enum class Modifier { + BOLD = 1, + FAINT = 2, + ITALIC = 3, + UNDERLINE = 4, + BLINK = 5, + REVERSE = 7, + HIDDEN = 8 + }; + + // TODO: move the impl to a module impl details + [[nodiscard]] constexpr std::string __stylize_impl(const std::string &text, + const Color color, + std::vector &&modifiers) { + // The color code will depend on whether we are using a standard ANSI color + // or an extended color from the 256-color palette. + std::string colorCode = ""; + if (static_cast(color) >= 30 && static_cast(color) <= 37) { + // Standard ANSI colors are within the range of 30-37. + // These are directly used following the escape sequence "\033[". + colorCode = "\033[" + std::to_string(static_cast(color)) + "m"; + } else { + // Colors outside the range of standard ANSI colors are treated as part + // of the extended 256-color palette. They require a prefix "38;5;" + // which signals the terminal to interpret the following number as a + // 256-palette index. + colorCode = + "\033[38;5;" + std::to_string(static_cast(color)) + "m"; + } + + std::string modifierCodes = ""; + + for (const auto &modifier : modifiers) { + modifierCodes += + "\033[" + std::to_string(static_cast(modifier)) + "m"; + } + + std::string resetCodes = ""; + for (std::size_t i = 0; i < modifiers.size(); ++i) { + resetCodes += "\033[0m"; + } + + return modifierCodes + colorCode + text + resetCodes + "\033[0m"; + } + +// Only text and color (no modifiers) +[[nodiscard]] constexpr std::string stylize(const std::string &text, const Color color) { + return __stylize_impl(text, color, {}); // Pass an empty vector for no modifiers +} + +// Single modifier overload +[[nodiscard]] constexpr std::string stylize(const std::string &text, + const Color color, + Modifier modifier) { + return __stylize_impl(text, color, std::vector{modifier}); +} + +// Multiple modifiers overload +[[nodiscard]] constexpr std::string stylize(const std::string &text, + const Color color, + std::initializer_list modifiers) { + return __stylize_impl(text, color, std::vector(modifiers)); } } // namespace zero::fmt diff --git a/zero/main.cpp b/zero/main.cpp index 8535787..852dbc1 100644 --- a/zero/main.cpp +++ b/zero/main.cpp @@ -1,7 +1,7 @@ import std; import zero; import collections; -import iterator; +// import iterator; import container; import type_info; import physics; @@ -13,7 +13,7 @@ import tsuite; // Forward decls void run_containers_examples(); -void run_output_iterator_examples(); +// void run_output_iterator_examples(); void run_quantities_examples(); void run_formatter_and_stylize_examples(); void run_print_examples(); @@ -58,8 +58,8 @@ int main() { // run_containers_examples(); // run_output_iterator_examples(); // run_quantities_examples(); - // run_formatter_and_stylize_examples(); - // run_print_examples(); + run_formatter_and_stylize_examples(); + run_print_examples(); TEST_CASE("Multiplication Test", []() { int result = 5 * 3; @@ -89,7 +89,7 @@ int main() { // Forces a warning that alerts the user that the test will be discarded, since already // exists one with the same identifier in the given suite TEST_CASE(suite, "Addition Test", testAddition); - // Register a test case designed to fail, useful for testing the behavior + // Register a test case designed to fail, useful for testing the behavior // of RUN_TESTS with different failure modes. TEST_CASE(suite, "Subtraction Test", testSubtraction); @@ -135,7 +135,7 @@ void run_containers_examples() { std::cout << " - [const Container*] Value: " << value << std::endl; } -void run_output_iterator_examples() { +/* void run_output_iterator_examples() { std::cout << "Using output iterator with ostream: "; zero::iterator::legacy::output_iter out1(std::cout); out1 = 1; @@ -190,7 +190,7 @@ void run_output_iterator_examples() { std::cout << x << ' '; std::cout << '\n'; -} +} */ void run_quantities_examples() { using namespace zero::physics; diff --git a/zero/tests/math/numbers_tests.cpp b/zero/tests/math/numbers_tests.cpp new file mode 100644 index 0000000..6af060a --- /dev/null +++ b/zero/tests/math/numbers_tests.cpp @@ -0,0 +1,110 @@ +#include "numbers_tests.h" + +using namespace zero::math; + +TestSuite numbers_suite {"Numbers TS"}; + +/// Compile time tests for numbers +static_assert(Number); +static_assert(Number); +static_assert(Number); +static_assert(Number>); +static_assert(!Number); + +void numbers_tests() { + TEST_CASE(numbers_suite, "Testing the Numbers types construction", [] { + auto natural = Natural(1); + assertEquals(1, natural.number()); + + auto integer = Integer(7); + assertEquals(7, integer); + auto from_natural = Integer(natural); + assertEquals(1, from_natural); + + auto rational = Rational(5, 2); + assertEquals(5, rational.numerator()); + assertEquals(2, rational.denominator()); + + auto real = Real(10.); + assertEquals(10., real.number()); + + auto real_from_integer = Real(integer); + assertEquals(7, real_from_integer.number()); + + auto real_from_rational = Real(rational); + assertEquals(rational, real_from_rational.number()); + }); + + TEST_CASE(numbers_suite, "Testing the Numbers types equalities", [] { + assertEquals(Natural(1), Natural(1)); + assertEquals(Integer(-1), Integer(-1)); + + assertEquals(Rational(1, 2), Rational(1, 2)); + // Equality check for equal ratio values returns false if they aren't the exact + // same rational number + assertNotEquals(Rational(1, 2), Rational(2, 4)); + }); + + TEST_CASE(numbers_suite, "Arithmetic operations with Naturals", [] { + auto one_natural = Natural(5); + auto other_natural = Natural(2); + + assertEquals(7, one_natural + other_natural); + assertEquals(3, one_natural - other_natural); + assertEquals(10, one_natural * other_natural); + assertEquals(2, one_natural / other_natural); // int division. By default, this operator is the same as the + // language defined, so it will truncate the integer division + // towards zero. This is the same for any non-rational Number type + }); + + TEST_CASE(numbers_suite, "Arithmetic operations with Integers", [] { + auto one_integer = Integer(20); + auto other_integer = Integer(10); + + assertEquals(30, one_integer + other_integer); + assertEquals(10, one_integer - other_integer); + assertEquals(-10, other_integer - one_integer); + assertEquals(200, one_integer * other_integer); + assertEquals(2, one_integer / other_integer); + }); + + TEST_CASE(numbers_suite, "Arithmetic operations with Rationals (like fractions)", [] { + auto one_rational = Rational(8, 2); + auto other_rational = Rational(4, 2); + + auto rational_addition = one_rational + other_rational; + assertEquals(Rational(12, 2), rational_addition); + + auto rational_subtraction = one_rational - other_rational; + assertEquals(Rational(4, 2), rational_subtraction); + + auto rational_multiplication = one_rational * other_rational; + assertEquals(Rational(32, 4), rational_multiplication); + + auto rational_division = one_rational / other_rational; + assertEquals(Rational(16, 8), rational_division); + }); + + TEST_CASE(numbers_suite, "Arithmetic operations with Rationals (unlike fractions)", [] { + auto one_rational = Rational(3, 2); + auto other_rational = Rational(5, 4); + + auto rational_addition = one_rational + other_rational; + assertEquals(Rational(11, 4), rational_addition); + + auto rational_subtraction = one_rational - other_rational; + assertEquals(Rational(1, 4), rational_subtraction); + + auto rational_multiplication = one_rational * other_rational; + assertEquals(Rational(15, 8), rational_multiplication); + + auto integer_times_rational = Integer(3) * other_rational; + assertEquals(Rational(15, 4), integer_times_rational); + + auto rational_times_integer = one_rational * Integer(7); + assertEquals(Rational(21, 2), rational_times_integer); + + auto rational_divided_by_integer = one_rational / Integer(7); + assertEquals(Rational(3, 14), rational_divided_by_integer); + }); +} diff --git a/zero/tests/math/numbers_tests.h b/zero/tests/math/numbers_tests.h new file mode 100644 index 0000000..c451bfd --- /dev/null +++ b/zero/tests/math/numbers_tests.h @@ -0,0 +1,12 @@ +/** + * Tests for the Numbers types + */ + +#pragma once + +import std; +import tsuite; +import math; + +extern TestSuite numbers_suite; +extern void numbers_tests(); diff --git a/zero/tests/test_main.cpp b/zero/tests/test_main.cpp index 9e31d3b..8a6cb64 100644 --- a/zero/tests/test_main.cpp +++ b/zero/tests/test_main.cpp @@ -8,10 +8,13 @@ import std; #include "./math/matrix_tests.h" +#include "./math/numbers_tests.h" //TEST_CASE( "Base tests entry point for The Zero Project", "[Zero Project]" ) {} int main() { matrix_tests(); - RUN_TESTS(); + numbers_tests(); + + RUN_TESTS(TestRunBehavior::CONTINUE_ON_ERROR); return 0; -} \ No newline at end of file +} diff --git a/zork_config/zork_local_windows.toml b/zork_config/zork_clang.toml similarity index 70% rename from zork_config/zork_local_windows.toml rename to zork_config/zork_clang.toml index 751b473..3f161a2 100644 --- a/zork_config/zork_local_windows.toml +++ b/zork_config/zork_clang.toml @@ -2,29 +2,32 @@ name = "Zero" authors = [ "Zero Day Code" ] compilation_db = true -project_root = "zero" +code_root = "zero" [compiler] cpp_compiler = "clang" -cpp_standard = "2b" -std_lib = "LIBCPP" # Care, you must have installed LIBC++, which doesn't come by default with mingw -# Consider to use STDLIBCPP if you don't plan to use LLVM's STD library implementation +driver_path = "clang++" # This binary is soft linked and included in our local path +cpp_standard = "23" +std_lib = "LIBCPP" +# std_lib_installed_dir = "/usr/local/share/libc++/v1" +std_lib_installed_dir = "C:/msys64/clang64/share/libc++/v1" extra_args = [ '-Werror', '-Wall', '-Wpedantic', '-pedantic', '-Wextra', '-Wconversion', '-Wfloat-conversion', '-Wsign-conversion', '-Wshadow', '-Wnon-virtual-dtor', '-Wold-style-cast', '-Wcast-align', '-Wunused', '-Woverloaded-virtual', '-Wmisleading-indentation', '-Wnull-dereference', '-Wdouble-promotion', '-Wformat=2', '-Wimplicit-fallthrough', - '-Weffc++' + '-Weffc++', + '-Wno-deprecated-declarations' ] [build] output_dir = "./out" -[executable] -executable_name = "zero" +[targets.executable] +output_name = "zero" sources = [ "*.cpp" ] -[tests] -tests_executable_name = "zero_tests" +[targets.tests] +output_name = "zero_tests" sources = [ "tests/math/*.cpp", "tests/*.cpp" ] [modules] @@ -40,12 +43,12 @@ interfaces = [ { file = 'commons/concepts.cppm', dependencies = ['typedefs'] }, ### The testing suite - { file = 'test-suite/assertions.cppm', partition = { module = 'tsuite' } }, + { file = 'test-suite/assertions.cppm', partition = { module = 'tsuite' } }, # Root { file = 'test-suite/suite.cppm', module_name = 'tsuite' }, ### Iterator library - { file = 'iterators/internal/iterator_detail.cpp', partition = { module = 'iterator', partition_name = 'detail' } }, + { file = 'iterators/internal/iterator_detail.cpp', partition = { module = 'iterator', partition_name = 'detail', internal = true } }, { file = 'iterators/iterator_concepts.cppm', partition = { module = 'iterator', partition_name = 'concepts' } }, # Modern { file = 'iterators/iterator_facade.cppm', partition = { module = 'iterator' } }, @@ -57,13 +60,17 @@ interfaces = [ # Root { file = 'iterators/iterator.cppm' }, - ### The collections/containers librar + # The collections/containers library { file = 'collections/container.cppm', dependencies = ['type_info'] }, { file = 'collections/array.cppm', dependencies = ['typedefs', 'concepts', 'iterator', 'container'] }, # Root { file = 'collections/collections.cppm', dependencies = ['array'] }, ### Math library + # Symbols + { file = 'math/symbols.cppm', module_name = 'math.symbols' }, + # Numbers - shared + { file = 'math/general.cppm', partition = { module = 'math', partition_name = 'general' } }, # The operations library { file = 'math/ops/arithmetic.cppm', partition = { module = 'math.ops', partition_name = 'arithmetic' } }, { file = 'math/ops/algebraic.cppm', partition = { module = 'math.ops', partition_name = 'algebraic' } }, @@ -71,8 +78,13 @@ interfaces = [ # The linear algebra library { file = 'math/linear_algebra/matrix.cppm', partition = { module = 'math.linear_algebra', partition_name = 'matrix' } }, { file = 'math/linear_algebra/root.cppm', module_name = 'math.linear_algebra' }, - # General - { file = 'math/symbols.cppm', module_name = 'math.symbols' }, + # Numbers - Core + { file = 'math/numbers/naturals.cppm', partition = { module = 'math', partition_name = 'numbers.naturals' } }, + { file = 'math/numbers/integers.cppm', partition = { module = 'math', partition_name = 'numbers.integers' } }, + { file = 'math/numbers/rationals.cppm', partition = { module = 'math', partition_name = 'numbers.rationals' } }, + { file = 'math/numbers/reals.cppm', partition = { module = 'math', partition_name = 'numbers.reals' } }, + { file = 'math/numbers/detail.cpp', partition = { module = 'math', partition_name = 'numbers.detail', is_internal_partition = true } }, + { file = 'math/numbers/numbers.cppm', partition = { module = 'math', partition_name = 'numbers' } }, # Root { file = 'math/math.cppm' }, @@ -92,4 +104,3 @@ interfaces = [ # The main interface of the project { file = 'zero.cppm' } ] - diff --git a/zork_config/zork_gh_linux.toml b/zork_config/zork_gh_linux.toml index 46ad871..83eb5b6 100644 --- a/zork_config/zork_gh_linux.toml +++ b/zork_config/zork_gh_linux.toml @@ -1,38 +1,54 @@ [project] name = "Zero" -authors = [ "Zero Day Code" ] +authors = ["Zero Day Code"] compilation_db = true -project_root = "zero" +code_root = "zero" [compiler] cpp_compiler = "clang" -driver_path = "clang++-15" +driver_path = "clang++" cpp_standard = "2b" std_lib = "libc++" +# std_lib_installed_dir = "/usr/lib/llvm-19/share/libc++/v1" extra_args = [ - "-fmodules", '-Wno-error=unused-command-line-argument', - '-Werror', '-Wall', '-Wpedantic', '-pedantic', '-Wextra', '-Wconversion', '-Wfloat-conversion', '-Wsign-conversion', - '-Wshadow', '-Wnon-virtual-dtor', '-Wold-style-cast', '-Wcast-align', '-Wunused', '-Woverloaded-virtual', - '-Wmisleading-indentation', '-Wnull-dereference', '-Wdouble-promotion', '-Wformat=2', '-Wimplicit-fallthrough', - '-Weffc++' + # '-Werror', + # '-Wall', + '-Wpedantic', + '-pedantic', + '-Wextra', + '-Wconversion', + '-Wfloat-conversion', + '-Wsign-conversion', + '-Wshadow', + '-Wnon-virtual-dtor', + '-Wold-style-cast', + '-Wcast-align', + '-Wunused', + '-Woverloaded-virtual', + '-Wmisleading-indentation', + '-Wnull-dereference', + '-Wdouble-promotion', + '-Wformat=2', + '-Wimplicit-fallthrough', + '-Weffc++', ] [build] output_dir = "./out" -[executable] -executable_name = "zero" -sources = [ "*.cpp" ] +[targets.executable] +output_name = "zero" +sources = ["*.cpp"] -[tests] -tests_executable_name = "zero_tests" -sources = [ "tests/**/*.cpp", "tests/math/*.cpp", "tests/*.cpp" ] +[targets.tests] +output_name = "zero_tests" +sources = ["tests/math/*.cpp", "tests/*.cpp"] [modules] base_ifcs_dir = "ifc" interfaces = [ - { file = 'commons/typedefs.cppm' }, + { file = 'commons/typedefs.cppm' }, { file = 'text/str_manip.cppm' }, { file = 'text/formatter.cppm' }, { file = 'text/stylizer.cppm' }, @@ -42,12 +58,12 @@ interfaces = [ { file = 'commons/concepts.cppm', dependencies = ['typedefs'] }, ### The testing suite - { file = 'test-suite/assertions.cppm', partition = { module = 'tsuite' } }, + { file = 'test-suite/assertions.cppm', partition = { module = 'tsuite' } }, # Root { file = 'test-suite/suite.cppm', module_name = 'tsuite' }, ### Iterator library - { file = 'iterators/internal/iterator_detail.cpp', partition = { module = 'iterator', partition_name = 'detail' } }, + { file = 'iterators/internal/iterator_detail.cpp', partition = { module = 'iterator', partition_name = 'detail', internal = true } }, { file = 'iterators/iterator_concepts.cppm', partition = { module = 'iterator', partition_name = 'concepts' } }, # Modern { file = 'iterators/iterator_facade.cppm', partition = { module = 'iterator' } }, @@ -59,12 +75,17 @@ interfaces = [ # Root { file = 'iterators/iterator.cppm' }, - # The collections/containers librar - { file = 'collections/container.cppm', dependencies = ['type_info'] }, - { file = 'collections/array.cppm', dependencies = ['typedefs', 'concepts', 'iterator', 'container'] }, + # The collections/containers library + { file = 'collections/container.cppm', dependencies = ['type_info'] }, + { file = 'collections/array.cppm', dependencies = ['typedefs', 'concepts', 'iterator', 'container'] }, + # Root { file = 'collections/collections.cppm', dependencies = ['array'] }, ### Math library + # Symbols + { file = 'math/symbols.cppm', module_name = 'math.symbols' }, + # Numbers - shared + { file = 'math/general.cppm', partition = { module = 'math', partition_name = 'general' } }, # The operations library { file = 'math/ops/arithmetic.cppm', partition = { module = 'math.ops', partition_name = 'arithmetic' } }, { file = 'math/ops/algebraic.cppm', partition = { module = 'math.ops', partition_name = 'algebraic' } }, @@ -72,11 +93,16 @@ interfaces = [ # The linear algebra library { file = 'math/linear_algebra/matrix.cppm', partition = { module = 'math.linear_algebra', partition_name = 'matrix' } }, { file = 'math/linear_algebra/root.cppm', module_name = 'math.linear_algebra' }, - # General - { file = 'math/symbols.cppm', module_name = 'math.symbols' }, + # Numbers - Core + { file = 'math/numbers/naturals.cppm', partition = { module = 'math', partition_name = 'numbers.naturals' } }, + { file = 'math/numbers/integers.cppm', partition = { module = 'math', partition_name = 'numbers.integers' } }, + { file = 'math/numbers/rationals.cppm', partition = { module = 'math', partition_name = 'numbers.rationals' } }, + { file = 'math/numbers/reals.cppm', partition = { module = 'math', partition_name = 'numbers.reals' } }, + { file = 'math/numbers/detail.cpp', partition = { module = 'math', partition_name = 'numbers.detail', is_internal_partition = true } }, + { file = 'math/numbers/numbers.cppm', partition = { module = 'math', partition_name = 'numbers' } }, # Root { file = 'math/math.cppm' }, - + ### The physics library # The quantities library { file = 'physics/quantities/internal/quantities_detail.cpp', partition = { module = 'physics.quantities', partition_name = 'quantities.detail' } }, @@ -93,4 +119,3 @@ interfaces = [ # The main interface of the project { file = 'zero.cppm' } ] - diff --git a/zork_config/zork_local_linux.toml b/zork_config/zork_windows_msvc.toml similarity index 73% rename from zork_config/zork_local_linux.toml rename to zork_config/zork_windows_msvc.toml index 693ce8f..8f56889 100644 --- a/zork_config/zork_local_linux.toml +++ b/zork_config/zork_windows_msvc.toml @@ -1,30 +1,24 @@ [project] name = "Zero" authors = [ "Zero Day Code" ] -compilation_db = true -project_root = "zero" +compilation_db = false +code_root = "zero" [compiler] -cpp_compiler = "clang" -driver_path = "clang++-16" # This binary is soft linked and included in our local path -cpp_standard = "2b" -std_lib = "LIBCPP" -extra_args = [ - '-Werror', '-Wall', '-Wpedantic', '-pedantic', '-Wextra', '-Wconversion', '-Wfloat-conversion', '-Wsign-conversion', - '-Wshadow', '-Wnon-virtual-dtor', '-Wold-style-cast', '-Wcast-align', '-Wunused', '-Woverloaded-virtual', - '-Wmisleading-indentation', '-Wnull-dereference', '-Wdouble-promotion', '-Wformat=2', '-Wimplicit-fallthrough', - '-Weffc++' -] +cpp_compiler = "msvc" +cpp_standard = "latest" +# extra_args = [ +# ] [build] output_dir = "./out" -[executable] -executable_name = "zero" +[targets.executable] +output_name = "zero" sources = [ "*.cpp" ] -[tests] -tests_executable_name = "zero_tests" +[targets.tests] +output_name = "zero_tests" sources = [ "tests/math/*.cpp", "tests/*.cpp" ] [modules] @@ -39,13 +33,13 @@ interfaces = [ { file = 'types/type_traits.cppm' }, { file = 'commons/concepts.cppm', dependencies = ['typedefs'] }, -# ### The testing suite + ### The testing suite { file = 'test-suite/assertions.cppm', partition = { module = 'tsuite' } }, # Root { file = 'test-suite/suite.cppm', module_name = 'tsuite' }, ### Iterator library - { file = 'iterators/internal/iterator_detail.cpp', partition = { module = 'iterator', partition_name = 'detail' } }, + { file = 'iterators/internal/iterator_detail.cpp', partition = { module = 'iterator', partition_name = 'detail', is_internal_partition = true } }, { file = 'iterators/iterator_concepts.cppm', partition = { module = 'iterator', partition_name = 'concepts' } }, # Modern { file = 'iterators/iterator_facade.cppm', partition = { module = 'iterator' } }, @@ -57,13 +51,17 @@ interfaces = [ # Root { file = 'iterators/iterator.cppm' }, - # The collections/containers librar + ### The collections/containers librar { file = 'collections/container.cppm', dependencies = ['type_info'] }, { file = 'collections/array.cppm', dependencies = ['typedefs', 'concepts', 'iterator', 'container'] }, # Root { file = 'collections/collections.cppm', dependencies = ['array'] }, ### Math library + # Symbols + { file = 'math/symbols.cppm', module_name = 'math.symbols' }, + # Numbers - shared + { file = 'math/general.cppm', partition = { module = 'math', partition_name = 'general' } }, # The operations library { file = 'math/ops/arithmetic.cppm', partition = { module = 'math.ops', partition_name = 'arithmetic' } }, { file = 'math/ops/algebraic.cppm', partition = { module = 'math.ops', partition_name = 'algebraic' } }, @@ -71,9 +69,14 @@ interfaces = [ # The linear algebra library { file = 'math/linear_algebra/matrix.cppm', partition = { module = 'math.linear_algebra', partition_name = 'matrix' } }, { file = 'math/linear_algebra/root.cppm', module_name = 'math.linear_algebra' }, - # General - { file = 'math/symbols.cppm', module_name = 'math.symbols' }, - # Root + # Numbers - Core + { file = 'math/numbers/naturals.cppm', partition = { module = 'math', partition_name = 'numbers.naturals' } }, + { file = 'math/numbers/integers.cppm', partition = { module = 'math', partition_name = 'numbers.integers' } }, + { file = 'math/numbers/rationals.cppm', partition = { module = 'math', partition_name = 'numbers.rationals' } }, + { file = 'math/numbers/reals.cppm', partition = { module = 'math', partition_name = 'numbers.reals' } }, + { file = 'math/numbers/detail.cpp', partition = { module = 'math', partition_name = 'numbers.detail', is_internal_partition = true } }, + { file = 'math/numbers/numbers.cppm', partition = { module = 'math', partition_name = 'numbers' } }, + # Root { file = 'math/math.cppm' }, ### The physics library