From 07551ebe5afc84be206aa3dff3823d13ab728a5c Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Thu, 21 Mar 2024 11:41:36 +0900 Subject: [PATCH 01/13] refactoring to match P02993R0 --- README.md | 22 +- docs/api_reference.adoc | 124 ++++++------ docs/big_integer.adoc | 2 +- docs/motivation.adoc | 4 +- docs/overview.adoc | 123 ++++++------ include/safe.hpp | 2 +- include/safe/algorithm/accumulate.hpp | 6 +- include/safe/algorithm/irange.hpp | 2 +- include/safe/array.hpp | 10 +- include/safe/constant.hpp | 4 +- .../safe/{var.hpp => constrained_number.hpp} | 112 ++++++----- include/safe/detail/function.hpp | 14 +- include/safe/detail/fwd.hpp | 22 +- include/safe/detail/integral_type.hpp | 9 +- include/safe/detail/make_constant.hpp | 4 +- .../safe/detail/var_assign_static_assert.hpp | 4 +- include/safe/dsl.hpp | 18 +- include/safe/dsl/abs.hpp | 6 +- include/safe/dsl/add.hpp | 8 +- include/safe/dsl/bit_width.hpp | 6 +- include/safe/dsl/bitwise_and.hpp | 6 +- include/safe/dsl/bitwise_invert.hpp | 6 +- include/safe/dsl/bitwise_or.hpp | 6 +- include/safe/dsl/bitwise_xor.hpp | 6 +- .../dsl/{ival.hpp => constrain_interval.hpp} | 8 +- .../safe/dsl/{mask.hpp => constrain_mask.hpp} | 28 +-- include/safe/dsl/constrain_set.hpp | 13 ++ include/safe/dsl/constraint_of.hpp | 17 ++ include/safe/dsl/detail/eval_intersection.hpp | 2 +- include/safe/dsl/detail/eval_union.hpp | 4 +- include/safe/dsl/divide.hpp | 6 +- include/safe/dsl/eval_fwd.hpp | 4 +- include/safe/dsl/fwd.hpp | 2 +- include/safe/dsl/is_equal.hpp | 2 +- include/safe/dsl/is_subset.hpp | 22 +- include/safe/dsl/is_superset.hpp | 2 +- include/safe/dsl/max.hpp | 6 +- include/safe/dsl/min.hpp | 6 +- include/safe/dsl/minus.hpp | 8 +- include/safe/dsl/modulo.hpp | 26 +-- include/safe/dsl/multiply.hpp | 6 +- include/safe/dsl/primitive.hpp | 8 +- include/safe/dsl/set.hpp | 13 -- include/safe/dsl/shift_left.hpp | 22 +- include/safe/dsl/shift_right.hpp | 22 +- include/safe/int.hpp | 18 +- include/safe/iostream.hpp | 4 +- include/safe/match.hpp | 12 +- include/safe/value.hpp | 4 +- test/safe/CMakeLists.txt | 6 +- .../array_constant_access_max_violation.cpp | 2 +- test/safe/{var.cpp => constrained_number.cpp} | 60 +++--- .../CMakeLists.txt | 0 .../assign_constant_max_violation.cpp | 0 .../assign_constant_min_violation.cpp | 0 .../assign_lhs_rhs_union.cpp | 10 + .../constrained_number/assign_lhs_union.cpp | 10 + .../constrained_number/assign_rhs_union.cpp | 10 + .../construct_constant_max_violation.cpp | 0 .../construct_constant_min_violation.cpp | 0 .../interval_larger_than_type.cpp | 0 test/safe/dsl.cpp | 140 ++++++------- test/safe/dsl/abs.cpp | 10 +- test/safe/dsl/add.cpp | 34 ++-- test/safe/dsl/bitwise_and.cpp | 64 +++--- test/safe/dsl/bitwise_invert.cpp | 6 +- test/safe/dsl/bitwise_or.cpp | 60 +++--- test/safe/dsl/bitwise_xor.cpp | 60 +++--- test/safe/dsl/constrain_mask.cpp | 190 ++++++++++++++++++ test/safe/dsl/divide.cpp | 4 +- test/safe/dsl/intersection.cpp | 12 +- test/safe/dsl/is_equal.cpp | 12 +- test/safe/dsl/is_subset.cpp | 22 +- test/safe/dsl/mask.cpp | 190 ------------------ test/safe/dsl/minus.cpp | 20 +- test/safe/match.cpp | 4 +- test/safe/var/assign_lhs_rhs_union.cpp | 10 - test/safe/var/assign_lhs_union.cpp | 10 - test/safe/var/assign_rhs_union.cpp | 10 - 79 files changed, 886 insertions(+), 861 deletions(-) rename include/safe/{var.hpp => constrained_number.hpp} (65%) rename include/safe/dsl/{ival.hpp => constrain_interval.hpp} (64%) rename include/safe/dsl/{mask.hpp => constrain_mask.hpp} (72%) create mode 100644 include/safe/dsl/constrain_set.hpp create mode 100644 include/safe/dsl/constraint_of.hpp delete mode 100644 include/safe/dsl/set.hpp rename test/safe/{var.cpp => constrained_number.cpp} (66%) rename test/safe/{var => constrained_number}/CMakeLists.txt (100%) rename test/safe/{var => constrained_number}/assign_constant_max_violation.cpp (100%) rename test/safe/{var => constrained_number}/assign_constant_min_violation.cpp (100%) create mode 100644 test/safe/constrained_number/assign_lhs_rhs_union.cpp create mode 100644 test/safe/constrained_number/assign_lhs_union.cpp create mode 100644 test/safe/constrained_number/assign_rhs_union.cpp rename test/safe/{var => constrained_number}/construct_constant_max_violation.cpp (100%) rename test/safe/{var => constrained_number}/construct_constant_min_violation.cpp (100%) rename test/safe/{var => constrained_number}/interval_larger_than_type.cpp (100%) create mode 100644 test/safe/dsl/constrain_mask.cpp delete mode 100644 test/safe/dsl/mask.cpp delete mode 100644 test/safe/var/assign_lhs_rhs_union.cpp delete mode 100644 test/safe/var/assign_lhs_union.cpp delete mode 100644 test/safe/var/assign_rhs_union.cpp diff --git a/README.md b/README.md index 10bc91e..dfd62b8 100644 --- a/README.md +++ b/README.md @@ -76,20 +76,20 @@ queue_data[enqueue_index] = 0xc001; ``` It is not possible to index into the `safe::array` with a raw integral value or -a `safe::var` with an interval outside the bounds of the array. +a `safe::constrained_number` with an interval outside the bounds of the array. ```c++ auto result_err = queue_data[4]; // <- COMPILE ERROR ``` -The `_u32` user-defined literal creates a `safe::var` type at compile time that +The `_u32` user-defined literal creates a `safe::constrained_number` type at compile time that is constrained to the single value given to it. ```c++ auto result = queue_data[4_u32]; // GOOD! ``` -Arithmetic operations generate a new requirement for the result-type. Adding `1` +Arithmetic operations generate a new constraint for the result-type. Adding `1` to `enqueue_index` means it could be one larger than the last element of the array. The *safe arithmetic* library correctly produces a compilation error. @@ -105,39 +105,39 @@ There are many ways to do this. For this queue implementation we want auto result = queue_data[(enqueue_index + 1_u32) % 1024_u32]; // GOOD! ``` -The `safe::array` advertises this requirement on the `safe::var` index +The `safe::array` advertises this constraint on the `safe::constrained_number` index parameter. ```c++ constexpr T & operator[]( - safe::var> index + safe::constrained_number, std::size_t> index ) { return storage[index.unsafe_value()]; } ``` -User-code can (and should) do the same: use `safe::var` to specify the +User-code can (and should) do the same: use `safe::constrained_number` to specify the requirements or assumptions about the input to the function. It is up to the caller to prove the values it is passing in are safe. More complex numerical requirements can be conveyed with *safe arithmetic*. For -example, a disjoint union of intervals can be used in a requirement to exclude +example, a disjoint union of intervals can be used in a constraint to exclude values or value ranges. For example, if a `0` is invalid, but all other values are OK, a union of -intervals can be used in the `safe::var`: +intervals can be used in the `safe::constrained_number`: ```c++ constexpr void dont_give_me_zero( - safe::var || safe::ival<1, 1000>> not_zero + safe::constrained_number || safe::constrain_interval<1, 1000>, int> not_zero ) { // ... do something really cool with this non-zero value ... } ``` -This makes it impossible to pass a value of `0` to this function. `safe::var` +This makes it impossible to pass a value of `0` to this function. `safe::constrained_number` can't be created or initialized with naked integral values, so how do we pass -in parameters? We either need to use a `safe::var` that is already proven +in parameters? We either need to use a `safe::constrained_number` that is already proven to satisfy the callees requirements, or we can use `safe::function`: ```c++ diff --git a/docs/api_reference.adoc b/docs/api_reference.adoc index 69f90d6..0f739f3 100644 --- a/docs/api_reference.adoc +++ b/docs/api_reference.adoc @@ -1,46 +1,46 @@ == API Reference -=== safe::var +=== safe::constrained_number ```c++ namespace safe { - template - struct var; + template + struct constrained_number; } ``` -`safe::var` wraps a runtime value with an associated `safe::dsl` requirement -describing the set of values it must be contained in. The requirement is used +`safe::constrained_number` wraps a runtime value with an associated `safe::dsl` constraint +describing the set of values it must be contained in. The constraint is used to check the value at runtime or prove at compile-time it is satisfied. ==== Member constants -===== `requirement` +===== `constraint` -The `safe::dsl` requirement describing allowed values. +The `safe::dsl` constraint describing allowed values. ==== Member functions ===== Constructor ```c++ -constexpr var() requires(requirement >= set<0>); +constexpr constrained_number() requires(constraint >= constrain_set<0>); ``` -Default constructor, only valid if the requirement allows a value of '0'. +Default constructor, only valid if the constraint allows a value of '0'. ```c++ -constexpr var(Var auto const & rhs); +constexpr constrained_number(any_constrained auto const & rhs); ``` -Construct a `safe::var` from another instance with potentially different, +Construct a `safe::constrained_number` from another instance with potentially different, but compatible requirements. Assignment safety is checked at compile time. ===== operator= ```c++ -constexpr auto operator=(Var auto & rhs) -> var &; +constexpr auto operator=(any_constrained auto & rhs) -> constrained_number &; ``` Assign value from another instance with potentially different, but compatible @@ -51,7 +51,7 @@ requirements. Assignment safety is checked at compile time. ===== operator{lt}={gt} ```c++ -[[nodiscard]] constexpr auto operator<=>(Var auto lhs, Var auto rhs) -> std::strong_ordering; +[[nodiscard]] constexpr auto operator<=>(any_constrained auto lhs, any_constrained auto rhs) -> std::strong_ordering; ``` Apply `operator{lt}={gt}` to `lhs` and `rhs` and return the result. @@ -59,7 +59,7 @@ Apply `operator{lt}={gt}` to `lhs` and `rhs` and return the result. ===== operator== ```c++ -[[nodiscard]] constexpr auto operator==(Var auto lhs, Var auto rhs) -> bool; +[[nodiscard]] constexpr auto operator==(any_constrained auto lhs, any_constrained auto rhs) -> bool; ``` Apply `operator==` to `lhs` and `rhs` and return the result. @@ -67,7 +67,7 @@ Apply `operator==` to `lhs` and `rhs` and return the result. ===== operator+ ```c++ -[[nodiscard]] constexpr auto operator+(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator+(any_constrained auto lhs, any_constrained auto rhs); ``` Add the underlying values of `lhs` and `rhs` and return the result. @@ -81,7 +81,7 @@ the underlying architecture, if all possible results will fit. ===== operator- ```c++ -[[nodiscard]] constexpr auto operator-(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator-(any_constrained auto lhs, any_constrained auto rhs); ``` Subtract the underlying values of `lhs` and `rhs` and return the result. @@ -93,7 +93,7 @@ Value types are demoted to a narrower type, down to the natural word size of the underlying architecture, if all possible results will fit. ```c++ -[[nodiscard]] constexpr auto operator-(Var auto v); +[[nodiscard]] constexpr auto operator-(any_constrained auto v); ``` Equivalent to `0_i - v`. @@ -101,7 +101,7 @@ Equivalent to `0_i - v`. ===== operator* ```c++ -[[nodiscard]] constexpr auto operator*(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator*(any_constrained auto lhs, any_constrained auto rhs); ``` Multiply the underlying values of `lhs` and `rhs` and return the result. @@ -112,7 +112,7 @@ overflow or underflow. No wraparound for signed or unsigned types. ===== operator/ ```c++ -[[nodiscard]] constexpr auto operator/(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator/(any_constrained auto lhs, any_constrained auto rhs); ``` Divide the underlying values of `lhs` and `rhs` and return the result. @@ -122,13 +122,13 @@ the underlying architecture, if all possible results will fit. [WARNING] ==== -A compilation error will result if the `rhs` requirement doesn't exclude '0'. +A compilation error will result if the `rhs` constraint doesn't exclude '0'. ==== ===== operator% ```c++ -[[nodiscard]] constexpr auto operator%(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator%(any_constrained auto lhs, any_constrained auto rhs); ``` Modulo the underlying values of `lhs` and `rhs` and return the result. @@ -138,13 +138,13 @@ the underlying architecture, if all possible results will fit. [WARNING] ==== -A compilation error will result if the `rhs` requirement doesn't exclude '0'. +A compilation error will result if the `rhs` constraint doesn't exclude '0'. ==== ===== operator<< ```c++ -[[nodiscard]] constexpr auto operator<<(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator<<(any_constrained auto lhs, any_constrained auto rhs); ``` Shift `lhs` left by `rhs` bit positions and return the result. @@ -162,7 +162,7 @@ enough to represent the value. ===== operator>> ```c++ -[[nodiscard]] constexpr auto operator>>(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator>>(any_constrained auto lhs, any_constrained auto rhs); ``` Shift `lhs` right by `rhs` bit positions and return the result. @@ -172,14 +172,14 @@ the underlying architecture, if all possible results will fit. [WARNING] ==== -A compilation error will result if the `rhs` requirement doesn't exclude +A compilation error will result if the `rhs` constraint doesn't exclude all negative numbers or numbers larger than the bit width of `lhs`. ==== ===== operator| ```c++ -[[nodiscard]] constexpr auto operator|(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator|(any_constrained auto lhs, any_constrained auto rhs); ``` Bitwise OR the underlying values of `lhs` and `rhs` and return the result. @@ -189,7 +189,7 @@ The resulting value type is the wider type of `lhs` and `rhs`. ===== operator& ```c++ -[[nodiscard]] constexpr auto operator&(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator&(any_constrained auto lhs, any_constrained auto rhs); ``` Bitwise AND the underlying values of `lhs` and `rhs` and return the result. @@ -199,7 +199,7 @@ The resulting value type is the narrower type of `lhs` and `rhs`. ===== operator^ ```c++ -[[nodiscard]] constexpr auto operator^(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto operator^(any_constrained auto lhs, any_constrained auto rhs); ``` Bitwise XOR the underlying values of `lhs` and `rhs` and return the result. @@ -209,7 +209,7 @@ The resulting value type is the wider type of `lhs` and `rhs`. ===== abs ```c++ -[[nodiscard]] constexpr auto abs(Var auto v); +[[nodiscard]] constexpr auto abs(any_constrained auto v); ``` Calculate the absolute value of `v` and return the result. @@ -217,7 +217,7 @@ Calculate the absolute value of `v` and return the result. ===== bit_width ```c++ -[[nodiscard]] constexpr auto bit_width(Var auto v); +[[nodiscard]] constexpr auto bit_width(any_constrained auto v); ``` Calculate the bit width of `v` and return the result. @@ -228,7 +228,7 @@ the underlying architecture, if all possible results will fit. ===== min ```c++ -[[nodiscard]] constexpr auto min(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto min(any_constrained auto lhs, any_constrained auto rhs); ``` Calculate the minimum of `lhs` and `rhs` and return the result. @@ -238,7 +238,7 @@ The resulting value type is the narrower type of `lhs` and `rhs`. ===== max ```c++ -[[nodiscard]] constexpr auto max(Var auto lhs, Var auto rhs); +[[nodiscard]] constexpr auto max(any_constrained auto lhs, any_constrained auto rhs); ``` Calculate the maximum of `lhs` and `rhs` and return the result. @@ -249,9 +249,9 @@ The resulting value type is the wider type of `lhs` and `rhs`. ```c++ [[nodiscard]] constexpr auto clamp( - Var auto value, - Var auto min_val, - Var auto max_val + any_constrained auto value, + any_constrained auto min_val, + any_constrained auto max_val ); ``` @@ -262,8 +262,8 @@ The resulting value type is the underlying type of `max_val`. ```c++ [[nodiscard]] constexpr auto clamp( auto unsafe_value, - Var auto min_val, - Var auto max_val + any_constrained auto min_val, + any_constrained auto max_val ); ``` @@ -304,7 +304,7 @@ Return true if the set of numbers represented by the requirements `A` and stem:[A subseteq B] -Return true if the set of numbers represented by the requirement `rhs` contains +Return true if the set of numbers represented by the constraint `rhs` contains all the numbers `lhs` does. ===== operator{gt}= @@ -315,7 +315,7 @@ all the numbers `lhs` does. stem:[A supe B] -Return true if the set of numbers represented by the requirement `lhs` contains +Return true if the set of numbers represented by the constraint `lhs` contains all the numbers `rhs` does. ===== operator|| @@ -368,7 +368,7 @@ stem:[{a / b \| a in A, b in B}] [WARNING] ==== -A compilation error will result if the `rhs` requirement doesn't exclude '0'. +A compilation error will result if the `rhs` constraint doesn't exclude '0'. ==== ===== operator% @@ -381,7 +381,7 @@ stem:[{a % b \| a in A, b in B}] [WARNING] ==== -A compilation error will result if the `rhs` requirement doesn't exclude '0'. +A compilation error will result if the `rhs` constraint doesn't exclude '0'. ==== ===== abs @@ -481,7 +481,7 @@ Member functions are same as `std::array` with the following exceptions: 1. No C++ exceptions will be thrown by any member function. 2. `data()` is intentionally omitted. 3. `operator[](pos)` and `at(pos)` have different signatures to support indexing by - `safe:var`. + `safe:constrained_number`. Only member functions significantly different from `std::array` are documented here. @@ -489,8 +489,8 @@ here. ===== operator[] ```c++ -constexpr auto operator[](var> pos) -> reference; -constexpr auto operator[](var> pos) const -> const_reference; +constexpr auto operator[](constrained_number> pos) -, size_type> reference; +constexpr auto operator[](constrained_number> pos) const -, size_type> const_reference; ``` Access element at `pos`. The value of `pos` must be proven to be within the @@ -499,8 +499,8 @@ range of the array at compile-time. ===== at ```c++ -constexpr auto at(var> pos) -> reference; -constexpr auto at(var> pos) const -> const_reference; +constexpr auto at(constrained_number> pos) -, size_type> reference; +constexpr auto at(constrained_number> pos) const -, size_type> const_reference; ``` Identical to `operator[]`. Access element at `pos`. The value of `pos` must be @@ -509,8 +509,8 @@ proven to be within the range of the array at compile-time. === safe::match -Use runtime checking to safely encapsulate values into `safe::var`s. -Automatically infer `safe::var` return type from multiple possible +Use runtime checking to safely encapsulate values into `safe::constrained_number`s. +Automatically infer `safe::constrained_number` return type from multiple possible return values. ==== Examples @@ -561,7 +561,7 @@ This implementation ensures the return type of `char_to_ord` is `ival_s32<0, 15>`. This minimal interval was automatically calculated by `safe::match`. This is the power of `safe::match`: check runtime values are within a specified set of values, wrap the values in the corresponding -`safe::var` type, and optionally return a value whose `safe::var` requirement +`safe::constrained_number` type, and optionally return a value whose `safe::constrained_number` constraint is the union of the possible values from each branch. @@ -577,12 +577,12 @@ namespace safe { ``` Similar to boost `irange` and Python's `range`. An iterable range of -`safe::var` integers. +`safe::constrained_number` integers. ===== Discussion A drawback of Safe Arithmetic's compile-time checking strategy is apparent -when attempting to increment a `safe::var`. Adding `1` to a value could risk +when attempting to increment a `safe::constrained_number`. Adding `1` to a value could risk overflow, and so it results in a compilation error. There are some ways around this depending on the specific situation: @@ -592,7 +592,7 @@ There are some ways around this depending on the specific situation: 3. Use `safe::match` to conditionally assign the result. For a simple `for` loop, these options are not useful. `safe::irange` should be -used instead to iterate over a series of integers with a `safe::var` index +used instead to iterate over a series of integers with a `safe::constrained_number` index value. ===== Example @@ -628,7 +628,7 @@ namespace safe { std::input_iterator && std::is_same_v::value_type, ValueType> && std::regular_invocable && - Var) + any_constrained) constexpr auto accumulate( IterType first, IterType last, @@ -644,7 +644,7 @@ namespace safe { std::ranges::range && std::is_same_v, ValueType> && std::regular_invocable && - Var) + any_constrained) constexpr auto accumulate( RangeType && range, ValueType init = ValueType{}, @@ -654,20 +654,20 @@ namespace safe { Works like `std::accumulate`, with the following exceptions: -1. Only works with ranges of `safe::var`s. +1. Only works with ranges of `safe::constrained_number`s. 2. Runtime execution of the function will stop when either the end of the range is reached, or `max_iter` is reached. -3. The return type is a `safe::var` with the largest possible interval based on +3. The return type is a `safe::constrained_number` with the largest possible interval based on the type contained in the range and the `max_iter` value. ==== safe::reduce Works like `std::reduce`, with the following exceptions: -1. Only works with ranges of `safe::var`s. +1. Only works with ranges of `safe::constrained_number`s. 2. Runtime execution of the function will stop when either the end of the range is reached, or `max_iter` is reached. -3. The return type is a `safe::var` with the largest possible interval based on +3. The return type is a `safe::constrained_number` with the largest possible interval based on the type contained in the range and the `max_iter` value. 4. The operation over the value type must satisfy the `safe::Associative` and `safe::Commutative` concepts. @@ -683,7 +683,7 @@ namespace safe { std::input_iterator && std::is_same_v::value_type, ValueType> && std::regular_invocable && - Var && + any_constrained && Associative && Commutative) constexpr auto reduce( @@ -701,7 +701,7 @@ namespace safe { std::ranges::range && std::is_same_v, ValueType> && std::regular_invocable && - Var && + any_constrained && Associative && Commutative) constexpr auto reduce( @@ -720,7 +720,7 @@ namespace safe { std::input_iterator && std::is_same_v::value_type, ValueType> && std::regular_invocable && - Var && + any_constrained && Associative && Commutative) auto reduce( @@ -741,7 +741,7 @@ namespace safe { std::ranges::range && std::is_same_v, ValueType> && std::regular_invocable && - Var && + any_constrained && Associative && Commutative) auto reduce( diff --git a/docs/big_integer.adoc b/docs/big_integer.adoc index 60cd37d..b8b7f6f 100644 --- a/docs/big_integer.adoc +++ b/docs/big_integer.adoc @@ -22,7 +22,7 @@ are selected: underflow. 3. No C++ exceptions. Exceptional conditions will not be handled by `big_integer`. For user code, `big_integer` should always be wrapped in a - `safe::var` to provide protection from unsafe operations. + `safe::constrained_number` to provide protection from unsafe operations. 4. `big_integer` shall be signed and there will be no unsigned version. 5. Seamless interop with standard integral types. diff --git a/docs/motivation.adoc b/docs/motivation.adoc index 275ce12..7aaee47 100644 --- a/docs/motivation.adoc +++ b/docs/motivation.adoc @@ -79,8 +79,8 @@ differing dimensions are allowed and the resulting type represents a new unit and dimension. The Safe Arithmetic library also uses the {cpp} type system to enforce correct -operations by encoding the set of possible values in a `safe::var` 's type. -Operations on these `safe::var` s return new types with an updated set of +operations by encoding the set of possible values in a `safe::constrained_number` 's type. +Operations on these `safe::constrained_number` s return new types with an updated set of possible values. The actual runtime value is guaranteed to be within this set. The {cpp} type system essentially supports sophisticated static analysis diff --git a/docs/overview.adoc b/docs/overview.adoc index fe10f77..a816d60 100644 --- a/docs/overview.adoc +++ b/docs/overview.adoc @@ -4,26 +4,26 @@ === Safe Arithmetic Environment The Safe Arithmetic library uses the C++ type system to encode and enforce -requirements on values. A special template type, `safe::var` is used to contain +requirements on values. A special template type, `safe::constrained_number` is used to contain these values. ```c++ namespaced safe { template - struct var; + struct constrained_number; } ``` -Arithmetic, bitwise, and shift operators on `safe::var` values results in -the generation of a new `safe::var` with its requirements updated to represent -the set of possible values the result may contain. Operations on `safe::var` +Arithmetic, bitwise, and shift operators on `safe::constrained_number` values results in +the generation of a new `safe::constrained_number` with its requirements updated to represent +the set of possible values the result may contain. Operations on `safe::constrained_number` values are guaranteed to be safe at compile-time. There is no runtime overhead incurred. Only the desired operations are performed. -Operations on instances of `safe::var` forms a hermetically sealed context in +Operations on instances of `safe::constrained_number` forms a hermetically sealed context in which overflows, underflows, and division-by-zero are proven impossible by the Safe Arithmetic library implementation. If such a condition were possible -due to an arithmetic operation on a `safe::var`, then compilation would fail. +due to an arithmetic operation on a `safe::constrained_number`, then compilation would fail. This leaves two important questions, how to get values in and out of this "Safe Arithmetic Environment". @@ -31,8 +31,8 @@ Arithmetic Environment". === Creating Safe Values Safe literal values can be created using the `_i` user defined literal. It will -create a `safe::var` with the necessary integer type to contain the value and -a requirement that matches the value. Literal values larger than 64-bits are +create a `safe::constrained_number` with the necessary integer type to contain the value and +a constraint that matches the value. Literal values larger than 64-bits are implemented using an arbitrary precision integer type built into the Safe Arithmetic library. @@ -44,24 +44,24 @@ namespace safe::literals { ``` Safe versions of the C++ primitive integer types are available for declaring -runtime values. Each primitive integer type is wrapped in `safe::var` with a -requirement describing the range of that primitive type. +runtime values. Each primitive integer type is wrapped in `safe::constrained_number` with a +constraint describing the range of that primitive type. ```c++ namespace safe { // safe versions of C++ primitive integer types - using u8 = var; - using s8 = var; - using u16 = var; - using s16 = var; - using u32 = var; - using s32 = var; - using u64 = var; - using s64 = var; + using u8 = constrained_number<..., std::uint8_t>; + using s8 = constrained_number<..., std::int8_t>; + using u16 = constrained_number<..., std::uint16_t>; + using s16 = constrained_number<..., std::int16_t>; + using u32 = constrained_number<..., std::uint32_t>; + using s32 = constrained_number<..., std::int32_t>; + using u64 = constrained_number<..., std::uint64_t>; + using s64 = constrained_number<..., std::int64_t>; } ``` -Because each safe primitive integer type requirement's can contain all values +Because each safe primitive integer type constraint's can contain all values that are representable by the underlying type, it is safe to directly assign primitive values to instances of these safe primitive types. @@ -102,7 +102,7 @@ Safe Arithmetic library provides an arbitrary precision implementation, namespace safe { // safe arbitrary precision signed integer type template - using integer = var<...>; + using integer = constrained_number<...>; } ``` @@ -138,12 +138,12 @@ safe::u64 reg_result_3 = (hw_reg_1 + hw_reg2) & 0xFFFF'FFFF'FFFF'FFFF_i; `safe::match` is the only mechanism in the Safe Arithmetic library that will produce additional runtime overhead. It creates a callable object that may be -called with `safe::var` or naked integer values. It uses compile-time checks +called with `safe::constrained_number` or naked integer values. It uses compile-time checks if possible to match the given arguments with the `matchable_funcs` arguments. If compile-time checks are not possible for an argument, then the value is checked at runtime to determine if it satisfies the requirements for one of the `matchable_funcs`. It is analogous to a pattern matching switch statement where -the `matchable_funcs` arguments `safe::var` requirements are the patterns to +the `matchable_funcs` arguments `safe::constrained_number` requirements are the patterns to match the callable object's input arguments against. This is the recommended way of marshalling external integer values into a safe @@ -216,7 +216,7 @@ void event_count_interrupt_handler() { reference section. The final method of introducing values into the safe arithmetic environment is -through `unsafe_cast(value)`. It bypasses all compile-time and runtime +through `constraint_cast(value)`. It bypasses all compile-time and runtime safety checks and depends on the value to be proven to satisfy the requirements of `T` using mechanisms outside the visibility and scope of the Safe Arithmetic library. Its use is highly discouraged. The name is chosen to cause an uneasy @@ -224,11 +224,11 @@ feeling in programmers and clearly signal a red flag for code reviewers. ```c++ template -T unsafe_cast(auto value); +T constraint_cast(auto value); ``` -`unsafe_cast(value)` is used within the Safe Arithmetic library to ferry -values into instances of `safe::var` after proving it is safe to do so. It is +`constraint_cast(value)` is used within the Safe Arithmetic library to ferry +values into instances of `safe::constrained_number` after proving it is safe to do so. It is necessary for the library's construction. As always, an example is useful to illustrate how to use a particular function. @@ -238,7 +238,7 @@ std::uint16_t some_function(); void do_something_useful(safe::ival_u32<0, 1024> useful_value); // VERY DANGEROUS: Don't do this! -auto dangerous_value = unsafe_cast>(some_function()); +auto dangerous_value = constraint_cast>(some_function()); do_something_useful(dangerous_value); // SAFE: Use safe::match instead. This will only call 'do_something_useful' @@ -246,10 +246,10 @@ do_something_useful(dangerous_value); // 'useful_value'. If it doesn't match, the default callable will be invoked. safe::match(do_something_useful, [](){})(some_function()); -// SAFE: Don't use unsafe_cast(value), try almost everything else first. +// SAFE: Don't use constraint_cast(value), try almost everything else first. ``` -If you find a case where you feel you must use `unsafe_cast`, then maybe there +If you find a case where you feel you must use `constraint_cast`, then maybe there is a gap in the Safe Arithmetic API or an algorithm that is missing. Please let us know by filing an issue. @@ -257,30 +257,27 @@ us know by filing an issue. Extracting values out of the safe arithmetic environment is not dangerous or unsafe in itself, but it is important to be explicit when doing so. -`safe_cast(value)` is used to extract integer values out of `safe::vars`. +`static_cast(value)` is used to extract integer values out of `safe::constrained_numbers`. It is an acknowledgement by the programmer they are leaving the safe environment and must now take on the burden of ensuring safe arithmetic operations manually. It is also a clear indication for code reviewers to take a more critical look at any following integer operations. ```c++ -template -T safe_cast(auto value); - safe::ival_s32<-1000, 1000> my_safe_value = 42_i; -// SAFE: It's OK to use safe_cast to assign to a wider primitive type -auto innocent_value = safe_cast(my_safe_value); +// SAFE: It's OK to use static_cast to assign to a wider primitive type +auto innocent_value = static_cast(my_safe_value); -// COMPILE ERROR: A narrowing conversion is not allowed by safe_cast -auto another_innocent = safe_cast(my_safe_value); +// COMPILE ERROR: A narrowing conversion is not allowed by static_cast +auto another_innocent = static_cast(my_safe_value); ``` === Safe Arithmetic Requirements DSL The Requirements Domain-Specific Language is used to define the set of valid -values for a `safe::var` templated type. `safe_numerics` and +values for a `safe::constrained_number` templated type. `safe_numerics` and `bounded::integer` both use interval arithmetic at compile time to track the set of valid values. The Safe Arithmetic library works with intervals, sets, tristate bitmasks, and set operators like union, intersection, and difference @@ -298,14 +295,14 @@ safe::ival_s32<-100, 100> small_number{}; Which is equivalent to the following: ```c++ -safe::var> small_number = 0_i; +safe::constrained_number, std::int32_t> small_number = 0_i; ``` If we want to exclude '0' from the range, the DSL allows us to do that: ```c++ -using safe::ival; -safe::var || ival<1, 100>> small_nonzero_number = 1_i; +using safe::constrain_interval; +safe::constrained_number || constrain_interval<1, 100>, std::int32_t> small_nonzero_number = 1_i; ``` This enables the library to protect against divide-by-zero at compile-time. The @@ -319,40 +316,40 @@ auto result_1 = 10_i / small_number; auto result_2 = 10_i / small_nonzero_number; ``` -The DSL can be used by itself, outside of `safe::var`. This can be helpful to +The DSL can be used by itself, outside of `safe::constrained_number`. This can be helpful to illustrate the rules and capabilities of the DSL itself. -The assignment operator and constructors for `safe::var` that accept -another `safe::var` use set inequality operators to determine +The assignment operator and constructors for `safe::constrained_number` that accept +another `safe::constrained_number` use set inequality operators to determine whether it is safe or not. The right-hand-side argument's requirements must be a subset of the left-hand-side target. ```c++ -using safe::ival; +using safe::constrain_interval; -constexpr auto non_zero_req = ival<-100, -1> || ival<1, 100>; -constexpr auto small_num_req = ival<-100, 100>; +constexpr auto non_zero_req = constrain_interval<-100, -1> || constrain_interval<1, 100>; +constexpr auto small_num_req = constrain_interval<-100, 100>; // The `<=` operator is used for 'is subset of' static_assert(non_zero_req <= small_num_req); -safe::var non_zero = 1_i; +safe::constrained_number non_zero = 1_i; // The `<=` operator ensures this assignment is safe at compile-time -safe::var small_num = non_zero; +safe::constrained_number small_num = non_zero; ``` -When any operation is performed on a `safe::var` instance, the mirror operation +When any operation is performed on a `safe::constrained_number` instance, the mirror operation is performed on the requirements. ```c++ -using safe::ival; +using safe::constrain_interval; -constexpr auto one_to_ten_req = ival<1, 10>; -constexpr auto non_zero_req = ival<-100, -1> || ival<1, 100>; +constexpr auto one_to_ten_req = constrain_interval<1, 10>; +constexpr auto non_zero_req = constrain_interval<-100, -1> || constrain_interval<1, 100>; -safe::var a = 42_i; -safe::var b = 3_i; +safe::constrained_number a = 42_i; +safe::constrained_number b = 3_i; auto c = a * b; @@ -360,30 +357,30 @@ auto c = a * b; assert(c == 126_i); // static requirements are also updated as expected -static_assert(c.requirement == ival<-1000, -1> || ival<1, 1000>); +static_assert(c.constraint == constrain_interval<-1000, -1> || constrain_interval<1, 1000>); ``` -==== Requirement DSL Primitives +==== Constraint DSL Primitives |=== |Name |Definition |{cpp} | Description |Interval |stem:[[a, b]] -| ```safe::ival``` +| ```safe::constrain_interval``` | A set of values from a to b, inclusive. |Set |stem:[{a, b, c, ...}] -| ```safe::set``` +| ```safe::constrain_set``` | A set of explicitly defined values. |Mask | stem:[{x in NN \| 0 <= x < 2^n ^^ (x\ \&\ ~V) = C))}] -| ```safe::mask``` -| V is the variable bits mask. C is the constant bits mask. `safe::mask` +| ```safe::constrain_mask``` +| V is the variable bits mask. C is the constant bits mask. `safe::constrain_mask` produces a set of integers where the binary digits match C if the corresponding digits of V are unset. The binary digit places that are set in V are unconstrained in the elements of the produced set. @@ -391,7 +388,7 @@ unconstrained in the elements of the produced set. |=== -==== Requirement DSL Operators +==== Constraint DSL Operators |=== |Name |Definition |{cpp} Operator | Description diff --git a/include/safe.hpp b/include/safe.hpp index 3722100..b4e2e54 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -9,4 +9,4 @@ #include #include #include -#include +#include diff --git a/include/safe/algorithm/accumulate.hpp b/include/safe/algorithm/accumulate.hpp index 5bd2faa..2123472 100644 --- a/include/safe/algorithm/accumulate.hpp +++ b/include/safe/algorithm/accumulate.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -59,7 +59,7 @@ constexpr inline auto plus_op = [](auto a, auto b) { return a + b; }; template [[nodiscard]] constexpr inline auto accumulate(detail::iter_like auto first, auto last, auto init, auto op) { - constexpr auto req = decltype((*first).requirement){}; + constexpr auto req = decltype((*first).constraint){}; constexpr auto sum_req = detail::fold(req, op); using ret_num_t = decltype((*first).unsafe_value()); @@ -72,7 +72,7 @@ template iter_count++; } - return unsafe_cast>(sum); + return constraint_cast>(sum); } template diff --git a/include/safe/algorithm/irange.hpp b/include/safe/algorithm/irange.hpp index 67b6a6a..b8e8d3e 100644 --- a/include/safe/algorithm/irange.hpp +++ b/include/safe/algorithm/irange.hpp @@ -22,7 +22,7 @@ template struct irange { : parent_{parent}, value_{value}, end_{end} {} constexpr auto operator*() const -> ret_t { - return unsafe_cast(value_); + return constraint_cast(value_); } constexpr auto operator++() { diff --git a/include/safe/array.hpp b/include/safe/array.hpp index f093917..c87e4bb 100644 --- a/include/safe/array.hpp +++ b/include/safe/array.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -31,21 +31,21 @@ template struct array { constexpr explicit array(Us... values) : storage({values...}) {} [[nodiscard]] constexpr auto - operator[](var> pos) -> reference { + operator[](constrained_number, size_type> pos) -> reference { return storage[pos.unsafe_value()]; } [[nodiscard]] constexpr auto - operator[](var> pos) const -> const_reference { + operator[](constrained_number, size_type> pos) const -> const_reference { return storage[pos.unsafe_value()]; } - [[nodiscard]] constexpr auto at(var> pos) + [[nodiscard]] constexpr auto at(constrained_number, size_type> pos) -> reference { return storage[pos.unsafe_value()]; } - [[nodiscard]] constexpr auto at(var> pos) const + [[nodiscard]] constexpr auto at(constrained_number, size_type> pos) const -> const_reference { return storage[pos.unsafe_value()]; } diff --git a/include/safe/constant.hpp b/include/safe/constant.hpp index 6752948..a2ccd01 100644 --- a/include/safe/constant.hpp +++ b/include/safe/constant.hpp @@ -2,10 +2,10 @@ #include #include -#include +#include namespace safe { template -constexpr var> constant = +constexpr constrained_number, U> constant = detail::make_constant(); } \ No newline at end of file diff --git a/include/safe/var.hpp b/include/safe/constrained_number.hpp similarity index 65% rename from include/safe/var.hpp rename to include/safe/constrained_number.hpp index 4a62565..42f0cc1 100644 --- a/include/safe/var.hpp +++ b/include/safe/constrained_number.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -16,107 +17,114 @@ #include namespace safe { -template struct var { +template struct constrained_number { public: - constexpr static auto requirement = Requirement; + constexpr static auto constraint = Constraint; using value_type = T; - constexpr var() - requires(requirement >= ival<0, 0>) - : unsafe_value_(0) {} + constexpr constrained_number() + requires(constraint >= constrain_interval<0, 0>) + : _raw_value(0) {} template requires(std::is_convertible_v) // NOLINTNEXTLINE(google-explicit-constructor) - SAFE_INLINE constexpr var(unsafe_cast_ferry ferry) - : unsafe_value_{static_cast(ferry.value())} { - SAFE_ASSUME(requirement.check(unsafe_value_)); + SAFE_INLINE constexpr constrained_number(constraint_cast_ferry ferry) + : _raw_value{static_cast(ferry.value())} { + SAFE_ASSUME(constraint.check(_raw_value)); } template requires(std::integral && - (requirement >= detail::integral_type::requirement)) + (constraint >= detail::integral_type::constraint)) // NOLINTNEXTLINE(google-explicit-constructor) - SAFE_INLINE constexpr var(U rhs) : unsafe_value_{rhs} {} + SAFE_INLINE constexpr constrained_number(U rhs) : _raw_value{rhs} {} template - requires(requirement.check(rhs)) + requires(constraint.check(rhs)) // NOLINTNEXTLINE(google-explicit-constructor) - SAFE_INLINE constexpr var(std::integral_constant) - : unsafe_value_(rhs) // intentionally allowing narrowing conversions + SAFE_INLINE constexpr constrained_number(std::integral_constant) + : _raw_value(rhs) // intentionally allowing narrowing conversions {} // NOLINTNEXTLINE(google-explicit-constructor) - SAFE_INLINE constexpr var(Var auto const &rhs) - : unsafe_value_(rhs.unsafe_value()) // intentionally allowing narrowing + SAFE_INLINE constexpr constrained_number(any_constrained auto const &rhs) + : _raw_value(rhs.unsafe_value()) // intentionally allowing narrowing // conversions { static_assert_assign_requirements(*this, rhs); } - SAFE_INLINE constexpr auto operator=(Var auto const &rhs) -> var & { + SAFE_INLINE constexpr auto operator=(any_constrained auto const &rhs) -> constrained_number & { static_assert_assign_requirements(*this, rhs); - unsafe_value_ = rhs.unsafe_value(); + _raw_value = rhs.unsafe_value(); return *this; } - SAFE_INLINE constexpr auto operator=(Var auto &&rhs) -> var & { + SAFE_INLINE constexpr auto operator=(any_constrained auto &&rhs) -> constrained_number & { static_assert_assign_requirements(*this, rhs); - unsafe_value_ = rhs.unsafe_value(); + _raw_value = rhs.unsafe_value(); return *this; } + // template + // requires (constraint <= dsl::constraint_of) + // [[nodiscard]] SAFE_INLINE constexpr operator U() { + // SAFE_ASSUME(constraint.check(_raw_value)); + // return _raw_value; + // } + [[nodiscard]] SAFE_INLINE constexpr auto unsafe_value() const -> T { - SAFE_ASSUME(requirement.check(unsafe_value_)); - return unsafe_value_; + SAFE_ASSUME(constraint.check(_raw_value)); + return _raw_value; } using primitive_contract = detail::integral_type; static_assert(rhs_must_be_subset_of_lhs< - lhs_req, - rhs_req>::value); + lhs_req, + rhs_req>::value); /// @brief Do not use. Public to support use as non-type template parameter. - T unsafe_value_; + T _raw_value; }; -template constexpr bool at_least_one_var = (... or Var); +template constexpr bool at_least_one_var = (... or any_constrained); -[[nodiscard]] SAFE_INLINE constexpr auto to_var(Var auto v) -> Var auto { +[[nodiscard]] SAFE_INLINE constexpr auto to_var(any_constrained auto v) -> any_constrained auto { return v; } template [[nodiscard]] SAFE_INLINE constexpr auto to_var(std::integral_constant) - -> Var auto { + -> any_constrained auto { return detail::make_constant(); } -[[nodiscard]] SAFE_INLINE constexpr auto to_var(std::integral auto v) -> Var +[[nodiscard]] SAFE_INLINE constexpr auto to_var(std::integral auto v) -> any_constrained auto { return value(v); } template concept var_admissable = requires(T t) { - { to_var(t) } -> Var; + { to_var(t) } -> any_constrained; }; namespace detail { [[nodiscard]] SAFE_INLINE constexpr auto bin_op(auto op, var_admissable auto raw_lhs, var_admissable auto raw_rhs) { - Var auto const lhs = to_var(raw_lhs); - Var auto const rhs = to_var(raw_rhs); + any_constrained auto const lhs = to_var(raw_lhs); + any_constrained auto const rhs = to_var(raw_rhs); - auto result_req = dsl::detail::simp(op(lhs.requirement, rhs.requirement)); + auto result_req = dsl::detail::simp(op(lhs.constraint, rhs.constraint)); // FIXME: replace with embiggening strategy; the result type is // always large enough to contain its value auto result = op(lhs.unsafe_value(), rhs.unsafe_value()); - return unsafe_cast>(result); + return constraint_cast>(result); } } // namespace detail @@ -132,8 +140,8 @@ template return detail::bin_op(std::minus<>(), lhs, rhs); } -[[nodiscard]] SAFE_INLINE constexpr auto operator-(Var auto v) { - constexpr auto zero = var>{}; +[[nodiscard]] SAFE_INLINE constexpr auto operator-(any_constrained auto v) { + constexpr auto zero = constrained_number, decltype(v.unsafe_value())>{}; return zero - v; } @@ -201,55 +209,55 @@ template return to_var(raw_lhs).unsafe_value() == to_var(raw_rhs).unsafe_value(); } -[[nodiscard]] SAFE_INLINE constexpr auto abs(Var auto value) { +[[nodiscard]] SAFE_INLINE constexpr auto abs(any_constrained auto value) { using num_t = decltype(value.unsafe_value()); auto result = static_cast(std::abs(value.unsafe_value())); - auto result_req = dsl::detail::simp(safe::dsl::abs(value.requirement)); - return unsafe_cast>(result); + auto result_req = dsl::detail::simp(safe::dsl::abs(value.constraint)); + return constraint_cast>(result); } -[[nodiscard]] SAFE_INLINE constexpr auto bit_width(Var auto value) { +[[nodiscard]] SAFE_INLINE constexpr auto bit_width(any_constrained auto value) { using num_t = decltype(value.unsafe_value()); auto result = static_cast(std::bit_width(value.unsafe_value())); auto result_req = - dsl::detail::simp(safe::dsl::bit_width(value.requirement)); - return unsafe_cast>(result); + dsl::detail::simp(safe::dsl::bit_width(value.constraint)); + return constraint_cast>(result); } template requires(at_least_one_var) -[[nodiscard]] SAFE_INLINE constexpr auto min(L raw_lhs, R raw_rhs) -> Var auto { - Var auto const lhs = to_var(raw_lhs); - Var auto const rhs = to_var(raw_rhs); +[[nodiscard]] SAFE_INLINE constexpr auto min(L raw_lhs, R raw_rhs) -> any_constrained auto { + any_constrained auto const lhs = to_var(raw_lhs); + any_constrained auto const rhs = to_var(raw_rhs); using common_type = std::common_type_t; auto result = std::min(lhs.unsafe_value(), rhs.unsafe_value()); using result_t = decltype(result); auto result_req = - dsl::detail::simp(dsl::min(lhs.requirement, rhs.requirement)); - return unsafe_cast>(result); + dsl::detail::simp(dsl::min(lhs.constraint, rhs.constraint)); + return constraint_cast>(result); } template requires(at_least_one_var) -[[nodiscard]] SAFE_INLINE constexpr auto max(L raw_lhs, R raw_rhs) -> Var auto { - Var auto const lhs = to_var(raw_lhs); - Var auto const rhs = to_var(raw_rhs); +[[nodiscard]] SAFE_INLINE constexpr auto max(L raw_lhs, R raw_rhs) -> any_constrained auto { + any_constrained auto const lhs = to_var(raw_lhs); + any_constrained auto const rhs = to_var(raw_rhs); using common_type = std::common_type_t; auto result = std::max(lhs.unsafe_value(), rhs.unsafe_value()); using result_t = decltype(result); auto result_req = - dsl::detail::simp(dsl::max(lhs.requirement, rhs.requirement)); - return unsafe_cast>(result); + dsl::detail::simp(dsl::max(lhs.constraint, rhs.constraint)); + return constraint_cast>(result); } template requires(at_least_one_var) [[nodiscard]] SAFE_INLINE constexpr auto clamp(T value, MinT min_val, - MaxT max_val) -> Var auto { + MaxT max_val) -> any_constrained auto { return min(max(to_var(value), to_var(min_val)), to_var(max_val)); } } // namespace safe diff --git a/include/safe/detail/function.hpp b/include/safe/detail/function.hpp index b0b7b03..0442b33 100644 --- a/include/safe/detail/function.hpp +++ b/include/safe/detail/function.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include @@ -11,18 +11,18 @@ template struct runtime { [[nodiscard]] constexpr static auto check(InputT) -> bool { return true; } }; -template struct runtime { +template struct runtime { [[nodiscard]] constexpr static auto check(InputT const &input) -> bool { - return VarT::requirement.check(input); + return VarT::constraint.check(input); } }; -template struct runtime { +template struct runtime { [[nodiscard]] constexpr static auto check(InputVarT const &input) -> bool { - if constexpr (VarT::requirement >= InputVarT::requirement) { + if constexpr (VarT::constraint >= InputVarT::constraint) { return true; } else { - return VarT::requirement.check(input.unsafe_value()); + return VarT::constraint.check(input.unsafe_value()); }; } }; @@ -72,7 +72,7 @@ constexpr auto check(mp_list, ArgTs... args) -> bool { template [[nodiscard]] constexpr auto unwrap_var(T &&v) -> decltype(auto) { - if constexpr (Var>) { + if constexpr (any_constrained>) { return v.unsafe_value(); } else { return std::forward(v); diff --git a/include/safe/detail/fwd.hpp b/include/safe/detail/fwd.hpp index 4aa5da5..dd162bd 100644 --- a/include/safe/detail/fwd.hpp +++ b/include/safe/detail/fwd.hpp @@ -5,15 +5,15 @@ #endif namespace safe { -template struct var; +template struct constrained_number; template constexpr bool is_var_v = false; -template -constexpr bool is_var_v> = true; +template +constexpr bool is_var_v> = true; template -concept Var = is_var_v; +concept any_constrained = is_var_v; [[nodiscard]] constexpr inline auto value(auto value); @@ -22,12 +22,12 @@ template [[nodiscard]] constexpr inline auto make_constant(); } -template struct unsafe_cast_ferry { +template struct constraint_cast_ferry { private: T v; public: - SAFE_INLINE constexpr explicit(true) unsafe_cast_ferry(T new_value) + SAFE_INLINE constexpr explicit(true) constraint_cast_ferry(T new_value) : v{new_value} {} [[nodiscard]] SAFE_INLINE constexpr auto value() const -> T { return v; } @@ -36,13 +36,13 @@ template struct unsafe_cast_ferry { } // namespace safe template - requires(safe::Var) -[[nodiscard]] constexpr auto unsafe_cast(auto const &src) { - return T{safe::unsafe_cast_ferry{src}}; + requires(safe::any_constrained) +[[nodiscard]] constexpr auto constraint_cast(auto const &src) { + return T{safe::constraint_cast_ferry{src}}; } template - requires(!safe::Var) -[[nodiscard]] constexpr auto unsafe_cast(auto const &src) { + requires(!safe::any_constrained) +[[nodiscard]] constexpr auto constraint_cast(auto const &src) { return src; } diff --git a/include/safe/detail/integral_type.hpp b/include/safe/detail/integral_type.hpp index 56e8f2b..c0b03bf 100644 --- a/include/safe/detail/integral_type.hpp +++ b/include/safe/detail/integral_type.hpp @@ -1,13 +1,16 @@ #pragma once #include -#include +#include #include #include namespace safe::detail { template -using integral_type = var::lowest(), - std::numeric_limits::max()>>; +using integral_type = constrained_number< + safe::dsl::constrain_interval< + std::numeric_limits::lowest(), + std::numeric_limits::max()>, + T>; } \ No newline at end of file diff --git a/include/safe/detail/make_constant.hpp b/include/safe/detail/make_constant.hpp index c06f86c..2c8bb8d 100644 --- a/include/safe/detail/make_constant.hpp +++ b/include/safe/detail/make_constant.hpp @@ -1,11 +1,11 @@ #pragma once #include -#include +#include namespace safe::detail { template [[nodiscard]] constexpr inline auto make_constant() { - return unsafe_cast>>(value); + return constraint_cast, U>>(value); } } // namespace safe::detail \ No newline at end of file diff --git a/include/safe/detail/var_assign_static_assert.hpp b/include/safe/detail/var_assign_static_assert.hpp index 7a4cdbe..3820baf 100644 --- a/include/safe/detail/var_assign_static_assert.hpp +++ b/include/safe/detail/var_assign_static_assert.hpp @@ -15,7 +15,7 @@ template struct rhs_must_be_subset_of_lhs { constexpr inline void static_assert_assign_requirements(auto lhs, auto rhs) { static_assert( - rhs_must_be_subset_of_lhs, - rhs_req>::value); + rhs_must_be_subset_of_lhs, + rhs_req>::value); } } // namespace safe \ No newline at end of file diff --git a/include/safe/dsl.hpp b/include/safe/dsl.hpp index 37796e7..549f299 100644 --- a/include/safe/dsl.hpp +++ b/include/safe/dsl.hpp @@ -13,27 +13,27 @@ #include #include #include -#include -#include +#include +#include #include #include #include #include #include -#include +#include #include #include #include namespace safe { -using safe::dsl::ival; -using safe::dsl::ival_t; +using safe::dsl::constrain_interval; +using safe::dsl::constrain_interval_t; -using safe::dsl::mask; -using safe::dsl::mask_t; +using safe::dsl::constrain_mask; +using safe::dsl::constrain_mask_t; -using safe::dsl::set; -using safe::dsl::set_t; +using safe::dsl::constrain_set; +using safe::dsl::constrain_set_t; using safe::dsl::intersection_t; using safe::dsl::union_t; diff --git a/include/safe/dsl/abs.hpp b/include/safe/dsl/abs.hpp index f25ddb4..cdd4881 100644 --- a/include/safe/dsl/abs.hpp +++ b/include/safe/dsl/abs.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include #include @@ -24,7 +24,7 @@ template struct abs_t : public detail::unary_op { constexpr static bool straddles_zero = val::min < 0 && val::max > 0; - using type = ival_t; diff --git a/include/safe/dsl/add.hpp b/include/safe/dsl/add.hpp index 3e7628a..30426b4 100644 --- a/include/safe/dsl/add.hpp +++ b/include/safe/dsl/add.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include namespace safe::dsl { @@ -10,13 +10,13 @@ template struct add : public detail::binary_op {}; template struct add : public detail::binary_op { - using type = ival_t; + using type = constrain_interval_t; }; template struct add : public detail::binary_op { constexpr static auto value = LhsT::value + RhsT::value; - using type = mask_t; + using type = constrain_mask_t; }; template diff --git a/include/safe/dsl/bit_width.hpp b/include/safe/dsl/bit_width.hpp index a6d56e2..b4c1923 100644 --- a/include/safe/dsl/bit_width.hpp +++ b/include/safe/dsl/bit_width.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include #include @@ -14,7 +14,7 @@ template struct bit_width_t {}; template struct bit_width_t : public detail::unary_op { using val = detail::to_mask_t; - using type = ival_t; }; diff --git a/include/safe/dsl/bitwise_and.hpp b/include/safe/dsl/bitwise_and.hpp index 6c51add..e341a70 100644 --- a/include/safe/dsl/bitwise_and.hpp +++ b/include/safe/dsl/bitwise_and.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include namespace safe::dsl { @@ -14,7 +14,7 @@ struct bitwise_and : public detail::binary_op { using lhs = detail::to_mask_t; using rhs = detail::to_mask_t; constexpr static auto value = lhs::value & rhs::value; - using type = mask_t; + using type = constrain_mask_t; }; template diff --git a/include/safe/dsl/bitwise_invert.hpp b/include/safe/dsl/bitwise_invert.hpp index e1c7e2f..ac40462 100644 --- a/include/safe/dsl/bitwise_invert.hpp +++ b/include/safe/dsl/bitwise_invert.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include namespace safe::dsl { @@ -12,7 +12,7 @@ template struct bitwise_invert : public detail::unary_op { using mask_arg_t = detail::to_mask_t; constexpr static auto value = ~mask_arg_t::value; - using type = mask_t; + using type = constrain_mask_t; }; template diff --git a/include/safe/dsl/bitwise_or.hpp b/include/safe/dsl/bitwise_or.hpp index 57b91fb..6b3c450 100644 --- a/include/safe/dsl/bitwise_or.hpp +++ b/include/safe/dsl/bitwise_or.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include namespace safe::dsl { @@ -19,6 +19,6 @@ struct bitwise_or : public detail::binary_op { using lhs = detail::to_mask_t; using rhs = detail::to_mask_t; constexpr static auto value = lhs::value | rhs::value; - using type = mask_t; + using type = constrain_mask_t; }; } // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/bitwise_xor.hpp b/include/safe/dsl/bitwise_xor.hpp index ae0c46e..713778a 100644 --- a/include/safe/dsl/bitwise_xor.hpp +++ b/include/safe/dsl/bitwise_xor.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include namespace safe::dsl { @@ -14,7 +14,7 @@ struct bitwise_xor : public detail::binary_op { using lhs = detail::to_mask_t; using rhs = detail::to_mask_t; constexpr static auto value = lhs::value ^ rhs::value; - using type = mask_t; + using type = constrain_mask_t; }; template diff --git a/include/safe/dsl/ival.hpp b/include/safe/dsl/constrain_interval.hpp similarity index 64% rename from include/safe/dsl/ival.hpp rename to include/safe/dsl/constrain_interval.hpp index dd42a7e..b8bdd81 100644 --- a/include/safe/dsl/ival.hpp +++ b/include/safe/dsl/constrain_interval.hpp @@ -7,8 +7,8 @@ #include namespace safe::dsl { -template struct ival_t : public detail::primitive { - using type = ival_t; +template struct constrain_interval_t : public detail::primitive { + using type = constrain_interval_t; constexpr static auto min = Min; constexpr static auto max = Max; @@ -21,11 +21,11 @@ template struct ival_t : public detail::primitive { }; template -constexpr ival_t ival{}; +constexpr constrain_interval_t constrain_interval{}; template constexpr bool is_ival_v = false; -template constexpr bool is_ival_v> = true; +template constexpr bool is_ival_v> = true; template concept Interval = is_ival_v; diff --git a/include/safe/dsl/mask.hpp b/include/safe/dsl/constrain_mask.hpp similarity index 72% rename from include/safe/dsl/mask.hpp rename to include/safe/dsl/constrain_mask.hpp index c229fa4..78fa921 100644 --- a/include/safe/dsl/mask.hpp +++ b/include/safe/dsl/constrain_mask.hpp @@ -4,12 +4,12 @@ #include #include #include -#include +#include namespace safe::dsl { template -struct mask_t : public detail::primitive { - using type = mask_t; +struct constrain_mask_t : public detail::primitive { + using type = constrain_mask_t; constexpr static auto var_bits = VariableBits; constexpr static auto const_bits = ConstantBits; @@ -21,8 +21,8 @@ struct mask_t : public detail::primitive { }; template -constexpr mask_t - mask{}; +constexpr constrain_mask_t + constrain_mask{}; namespace detail { /** @@ -40,7 +40,7 @@ template [[nodiscard]] constexpr auto fill_in_bitmask(T value) { template struct to_ival {}; template -struct to_ival> { +struct to_ival> { /// lower bits that can change, including gaps between var_bits constexpr static auto var_portion = fill_in_bitmask(var_bits); @@ -50,11 +50,11 @@ struct to_ival> { constexpr static auto min = const_portion; constexpr static auto max = const_portion + (var_portion & var_bits); - using type = ival_t; + using type = constrain_interval_t; }; -template struct to_ival> { - using type = ival_t; +template struct to_ival> { + using type = constrain_interval_t; }; template using to_ival_t = typename to_ival::type; @@ -66,14 +66,14 @@ template using to_ival_t = typename to_ival::type; template struct to_mask {}; template -struct to_mask> { - using type = mask_t; +struct to_mask> { + using type = constrain_mask_t; }; -template struct to_mask> { +template struct to_mask> { constexpr static auto var_bits = fill_in_bitmask(max ^ min); constexpr static auto const_bits = min & ~var_bits; - using type = mask_t; + using type = constrain_mask_t; }; template using to_mask_t = typename to_mask::type; @@ -82,7 +82,7 @@ template using to_mask_t = typename to_mask::type; template constexpr bool is_mask_v = false; template -constexpr bool is_mask_v> = true; +constexpr bool is_mask_v> = true; template concept Mask = is_mask_v; diff --git a/include/safe/dsl/constrain_set.hpp b/include/safe/dsl/constrain_set.hpp new file mode 100644 index 0000000..6e314cb --- /dev/null +++ b/include/safe/dsl/constrain_set.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include +#include + +namespace safe::dsl { +template +using constrain_set_t = + union_t...>; + +template constexpr constrain_set_t constrain_set{}; +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/constraint_of.hpp b/include/safe/dsl/constraint_of.hpp new file mode 100644 index 0000000..229c802 --- /dev/null +++ b/include/safe/dsl/constraint_of.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include +#include + +namespace safe::dsl { + +template +constexpr auto constraint_of = + constrain_interval< + std::numeric_limits::lowest(), + std::numeric_limits::max() + >; + +} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/detail/eval_intersection.hpp b/include/safe/dsl/detail/eval_intersection.hpp index 19da6ea..e552945 100644 --- a/include/safe/dsl/detail/eval_intersection.hpp +++ b/include/safe/dsl/detail/eval_intersection.hpp @@ -17,7 +17,7 @@ struct interval_intersection_merge { using type = mp_push_back, - ival_t>; }; diff --git a/include/safe/dsl/detail/eval_union.hpp b/include/safe/dsl/detail/eval_union.hpp index 07ac7ee..ed3c602 100644 --- a/include/safe/dsl/detail/eval_union.hpp +++ b/include/safe/dsl/detail/eval_union.hpp @@ -14,13 +14,13 @@ using namespace boost::mp11; template struct interval_less; template - struct interval_less, ival_t> + struct interval_less, constrain_interval_t> : public std::integral_constant < bool, lhs_min {}; template using binary_interval_merge = - ival_t>( + constrain_interval_t>( LhsT::min, RhsT::min), std::max>( LhsT::max, RhsT::max)>; diff --git a/include/safe/dsl/divide.hpp b/include/safe/dsl/divide.hpp index c9d9658..0b966b6 100644 --- a/include/safe/dsl/divide.hpp +++ b/include/safe/dsl/divide.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -9,12 +9,12 @@ namespace safe::dsl { template struct divide : public detail::binary_op {}; template -struct divide, ival_t> +struct divide, constrain_interval_t> : public detail::binary_op { static_assert((rhs_min < 0 && rhs_max < 0) || (rhs_min > 0 && rhs_max > 0), "RHS of division operator must be guaranteed to not be '0'."); - using type = ival_t, detail::max>; diff --git a/include/safe/dsl/eval_fwd.hpp b/include/safe/dsl/eval_fwd.hpp index 3d9b7d4..9d24437 100644 --- a/include/safe/dsl/eval_fwd.hpp +++ b/include/safe/dsl/eval_fwd.hpp @@ -1,8 +1,8 @@ #pragma once #include -#include -#include +#include +#include #include #include diff --git a/include/safe/dsl/fwd.hpp b/include/safe/dsl/fwd.hpp index c0923d8..4bbcd48 100644 --- a/include/safe/dsl/fwd.hpp +++ b/include/safe/dsl/fwd.hpp @@ -8,7 +8,7 @@ struct unary_op {}; struct primitive {}; struct set_op {}; -// FIXME: make the min/max template vars take an arbitrary num of args +// FIXME: make the min/max template constrained_numbers take an arbitrary num of args template constexpr auto min2 = [] { if constexpr (lhs <= rhs) { diff --git a/include/safe/dsl/is_equal.hpp b/include/safe/dsl/is_equal.hpp index 7a58519..d84117a 100644 --- a/include/safe/dsl/is_equal.hpp +++ b/include/safe/dsl/is_equal.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include diff --git a/include/safe/dsl/is_subset.hpp b/include/safe/dsl/is_subset.hpp index 9161ad5..2bccfb1 100644 --- a/include/safe/dsl/is_subset.hpp +++ b/include/safe/dsl/is_subset.hpp @@ -1,14 +1,14 @@ #pragma once #include -#include -#include +#include +#include namespace safe::dsl { template struct is_subset {}; template -struct is_subset, ival_t> { +struct is_subset, constrain_interval_t> { using type = is_subset; constexpr static bool value = lhs_min >= rhs_min && lhs_max <= rhs_max; @@ -18,8 +18,8 @@ struct is_subset, ival_t> { template -struct is_subset, - mask_t> { +struct is_subset, + constrain_mask_t> { using type = is_subset; constexpr static bool lhs_has_no_var_bits_outside_rhs_var_bits = @@ -37,12 +37,12 @@ struct is_subset, template -struct is_subset, - mask_t> { +struct is_subset, + constrain_mask_t> { using type = is_subset; using rhs_ival = - detail::to_ival>; + detail::to_ival>; constexpr static bool value = detail::is_basic_mask(rhs_variable_bits) && lhs_min >= rhs_ival::min && @@ -53,12 +53,12 @@ struct is_subset, template -struct is_subset, - ival_t> { +struct is_subset, + constrain_interval_t> { using type = is_subset; using lhs_ival = - detail::to_ival>; + detail::to_ival>; constexpr static bool value = lhs_ival::min >= rhs_min && lhs_ival::max <= rhs_max; diff --git a/include/safe/dsl/is_superset.hpp b/include/safe/dsl/is_superset.hpp index 9570328..475b502 100644 --- a/include/safe/dsl/is_superset.hpp +++ b/include/safe/dsl/is_superset.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include namespace safe::dsl { template using is_superset = is_subset; diff --git a/include/safe/dsl/max.hpp b/include/safe/dsl/max.hpp index 5bb96f0..b42450d 100644 --- a/include/safe/dsl/max.hpp +++ b/include/safe/dsl/max.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -9,9 +9,9 @@ namespace safe::dsl { template struct max_t : public detail::binary_op {}; template -struct max_t, ival_t> +struct max_t, constrain_interval_t> : public detail::binary_op { - using type = ival_t< + using type = constrain_interval_t< std::max>( lhs_min, rhs_min), std::max>( diff --git a/include/safe/dsl/min.hpp b/include/safe/dsl/min.hpp index 3938f98..381fa3b 100644 --- a/include/safe/dsl/min.hpp +++ b/include/safe/dsl/min.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -9,9 +9,9 @@ namespace safe::dsl { template struct min_t : public detail::binary_op {}; template -struct min_t, ival_t> +struct min_t, constrain_interval_t> : public detail::binary_op { - using type = ival_t< + using type = constrain_interval_t< std::min>( lhs_min, rhs_min), std::min>( diff --git a/include/safe/dsl/minus.hpp b/include/safe/dsl/minus.hpp index 0136a7a..8c33b20 100644 --- a/include/safe/dsl/minus.hpp +++ b/include/safe/dsl/minus.hpp @@ -1,21 +1,21 @@ #pragma once #include -#include -#include +#include +#include namespace safe::dsl { template struct minus : public detail::binary_op {}; template struct minus : public detail::binary_op { - using type = ival_t; + using type = constrain_interval_t; }; template struct minus : public detail::binary_op { constexpr static auto value = LhsT::value - RhsT::value; - using type = mask_t; + using type = constrain_mask_t; }; template diff --git a/include/safe/dsl/modulo.hpp b/include/safe/dsl/modulo.hpp index 6799b19..31f9261 100644 --- a/include/safe/dsl/modulo.hpp +++ b/include/safe/dsl/modulo.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -45,46 +45,46 @@ template struct modulo : public detail::binary_op {}; template requires(0 < rhs_min && rhs_max < lhs_min) -struct modulo, ival_t> +struct modulo, constrain_interval_t> : public detail::binary_op { - using type = ival_t<0, rhs_max - 1>; + using type = constrain_interval_t<0, rhs_max - 1>; }; template requires(lhs_min > 0 && lhs_min == rhs_min && lhs_max == rhs_max) -struct modulo, ival_t> +struct modulo, constrain_interval_t> : public detail::binary_op { using type = detail::eval_t< - union_t, ival_t>>; + union_t, constrain_interval_t>>; }; template requires(0 <= lhs_min && lhs_max < rhs_min) -struct modulo, ival_t> +struct modulo, constrain_interval_t> : public detail::binary_op { - using type = ival_t; + using type = constrain_interval_t; }; template requires(lhs_max <= 0 && 0 < rhs_min && -lhs_min < rhs_min) -struct modulo, ival_t> +struct modulo, constrain_interval_t> : public detail::binary_op { - using type = ival_t; + using type = constrain_interval_t; }; template requires(0 < rhs_min && -lhs_min == rhs_max && -lhs_max == rhs_min) -struct modulo, ival_t> +struct modulo, constrain_interval_t> : public detail::binary_op { using type = detail::eval_t< - union_t, ival_t>>; + union_t, constrain_interval_t>>; }; template requires(0 < rhs_min && rhs_max < -lhs_max) -struct modulo, ival_t> +struct modulo, constrain_interval_t> : public detail::binary_op { - using type = ival_t<-(rhs_max - 1), 0>; + using type = constrain_interval_t<-(rhs_max - 1), 0>; }; template diff --git a/include/safe/dsl/multiply.hpp b/include/safe/dsl/multiply.hpp index aa6fe63..f437330 100644 --- a/include/safe/dsl/multiply.hpp +++ b/include/safe/dsl/multiply.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -9,10 +9,10 @@ namespace safe::dsl { template struct multiply : public detail::binary_op {}; template -struct multiply, ival_t> +struct multiply, constrain_interval_t> : public detail::binary_op { - using type = ival_t, detail::max>; diff --git a/include/safe/dsl/primitive.hpp b/include/safe/dsl/primitive.hpp index 22f2d8d..c4acf0c 100644 --- a/include/safe/dsl/primitive.hpp +++ b/include/safe/dsl/primitive.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include @@ -9,10 +9,10 @@ namespace safe::dsl::detail { template constexpr bool is_primitive_v = false; template -constexpr bool is_primitive_v> = true; +constexpr bool is_primitive_v> = true; template -constexpr bool is_primitive_v> = true; +constexpr bool is_primitive_v> = true; template concept Primitive = is_primitive_v; diff --git a/include/safe/dsl/set.hpp b/include/safe/dsl/set.hpp deleted file mode 100644 index 9251ba4..0000000 --- a/include/safe/dsl/set.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace safe::dsl { -template -using set_t = - union_t...>; - -template constexpr set_t set{}; -} // namespace safe::dsl \ No newline at end of file diff --git a/include/safe/dsl/shift_left.hpp b/include/safe/dsl/shift_left.hpp index 4bd6111..6b9d74f 100644 --- a/include/safe/dsl/shift_left.hpp +++ b/include/safe/dsl/shift_left.hpp @@ -2,16 +2,16 @@ #include #include -#include +#include namespace safe::dsl { template struct shift_left : public detail::binary_op {}; template -struct shift_left, ival_t> +struct shift_left, constrain_interval_t> : public detail::binary_op { - using type = ival_t; @@ -19,19 +19,19 @@ struct shift_left, ival_t> template requires(rhs_min == rhs_max) -struct shift_left, - ival_t> : public detail::binary_op { - using type = mask_t; +struct shift_left, + constrain_interval_t> : public detail::binary_op { + using type = constrain_mask_t; }; template requires(rhs_min < rhs_max) -struct shift_left, - ival_t> : public detail::binary_op { - using lhs_mask = mask_t; +struct shift_left, + constrain_interval_t> : public detail::binary_op { + using lhs_mask = constrain_mask_t; - using type = bitwise_or>, - shift_left>>; + using type = bitwise_or>, + shift_left>>; }; template diff --git a/include/safe/dsl/shift_right.hpp b/include/safe/dsl/shift_right.hpp index 76c16aa..73bdb0b 100644 --- a/include/safe/dsl/shift_right.hpp +++ b/include/safe/dsl/shift_right.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include @@ -11,9 +11,9 @@ template struct shift_right : public detail::binary_op {}; template -struct shift_right, ival_t> +struct shift_right, constrain_interval_t> : public detail::binary_op { - using type = ival_t> rhs_min, lhs_min >> rhs_max, + using type = constrain_interval_t> rhs_min, lhs_min >> rhs_max, lhs_max >> rhs_min, lhs_max >> rhs_max}), std::max({lhs_min >> rhs_min, lhs_min >> rhs_max, lhs_max >> rhs_min, lhs_max >> rhs_max})>; @@ -21,21 +21,21 @@ struct shift_right, ival_t> template requires(rhs_min == rhs_max) -struct shift_right, - ival_t> : public detail::binary_op { +struct shift_right, + constrain_interval_t> : public detail::binary_op { using type = - mask_t>(lhs_var_bits, rhs_max), operator>>(lhs_const_bits, + constrain_mask_t>(lhs_var_bits, rhs_max), operator>>(lhs_const_bits, rhs_max)>; }; template requires(rhs_min < rhs_max) -struct shift_right, - ival_t> : public detail::binary_op { - using lhs_mask = mask_t; +struct shift_right, + constrain_interval_t> : public detail::binary_op { + using lhs_mask = constrain_mask_t; - using type = bitwise_or>, - shift_right>>; + using type = bitwise_or>, + shift_right>>; }; template diff --git a/include/safe/int.hpp b/include/safe/int.hpp index e655a0e..87c6498 100644 --- a/include/safe/int.hpp +++ b/include/safe/int.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -38,21 +38,21 @@ using s64 = detail::integral_type; using namespace int_types; namespace interval_types { -template using ival_s8 = var>; +template using ival_s8 = constrained_number, int8_t>; -template using ival_u8 = var>; +template using ival_u8 = constrained_number, uint8_t>; -template using ival_s16 = var>; +template using ival_s16 = constrained_number, int16_t>; -template using ival_u16 = var>; +template using ival_u16 = constrained_number, uint16_t>; -template using ival_s32 = var>; +template using ival_s32 = constrained_number, int32_t>; -template using ival_u32 = var>; +template using ival_u32 = constrained_number, uint32_t>; -template using ival_s64 = var>; +template using ival_s64 = constrained_number, int64_t>; -template using ival_u64 = var>; +template using ival_u64 = constrained_number, uint64_t>; } // namespace interval_types using namespace interval_types; diff --git a/include/safe/iostream.hpp b/include/safe/iostream.hpp index 8d11d5e..368e1da 100644 --- a/include/safe/iostream.hpp +++ b/include/safe/iostream.hpp @@ -4,6 +4,6 @@ #include -auto operator<<(std::ostream &os, safe::Var auto var) -> std::ostream & { - return os << var.unsafe_value(); +auto operator<<(std::ostream &os, safe::any_constrained auto constrained_number) -> std::ostream & { + return os << constrained_number.unsafe_value(); } diff --git a/include/safe/match.hpp b/include/safe/match.hpp index bd1029a..b3d9127 100644 --- a/include/safe/match.hpp +++ b/include/safe/match.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -15,15 +15,15 @@ template struct common_ret { }; template - requires(Var> && ... && Var>) + requires(any_constrained> && ... && any_constrained>) struct common_ret { constexpr static auto ret_requirement = - (function_ret_t::requirement || ... || - function_ret_t::requirement); + (function_ret_t::constraint || ... || + function_ret_t::constraint); using value_type = typename function_ret_t::value_type; - using type = var; + using type = constrained_number; }; template using common_ret_t = typename common_ret::type; @@ -71,7 +71,7 @@ template func_arg_types{}, std::forward(args)...); if (args_satisfy_reqs) { - return func(unsafe_cast_ferry{ + return func(constraint_cast_ferry{ detail::unwrap_var(std::forward(args))}...); } // check the remaining functions' requirements diff --git a/include/safe/value.hpp b/include/safe/value.hpp index a7ddb78..1ecc7c7 100644 --- a/include/safe/value.hpp +++ b/include/safe/value.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include @@ -10,6 +10,6 @@ constexpr auto value(auto value) { using value_t = decltype(value); constexpr value_t min = std::numeric_limits::lowest(); constexpr value_t max = std::numeric_limits::max(); - return unsafe_cast>>(value); + return constraint_cast, value_t>>(value); } } // namespace safe \ No newline at end of file diff --git a/test/safe/CMakeLists.txt b/test/safe/CMakeLists.txt index 3b9c18c..629aaa6 100644 --- a/test/safe/CMakeLists.txt +++ b/test/safe/CMakeLists.txt @@ -1,5 +1,5 @@ add_subdirectory(array) -add_subdirectory(var) +add_subdirectory(constrained_number) function(add_test_suites) foreach(test_file ${ARGN}) @@ -31,7 +31,7 @@ add_test_suites( big_integer/detail/compare.cpp big_integer/detail/divides.cpp big_integer.cpp - var.cpp + constrained_number.cpp match.cpp array.cpp dsl/add.cpp @@ -40,7 +40,7 @@ add_test_suites( dsl/is_equal.cpp dsl/is_subset.cpp dsl/abs.cpp - dsl/mask.cpp + dsl/constrain_mask.cpp dsl/bitwise_and.cpp dsl/detail/triint.cpp dsl/bitwise_or.cpp diff --git a/test/safe/array/array_constant_access_max_violation.cpp b/test/safe/array/array_constant_access_max_violation.cpp index feddb99..291d8df 100644 --- a/test/safe/array/array_constant_access_max_violation.cpp +++ b/test/safe/array/array_constant_access_max_violation.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include using namespace safe::literals; diff --git a/test/safe/var.cpp b/test/safe/constrained_number.cpp similarity index 66% rename from test/safe/var.cpp rename to test/safe/constrained_number.cpp index 1e952fc..e036119 100644 --- a/test/safe/var.cpp +++ b/test/safe/constrained_number.cpp @@ -11,8 +11,8 @@ using namespace safe::interval_types; using namespace safe::int_types; using namespace safe::literals; -using safe::ival; -using safe::mask; +using safe::constrain_interval; +using safe::constrain_mask; template class safe_int_assign_test : public testing::Test {}; @@ -41,60 +41,60 @@ using std_int_types = ::testing::Types> const a = 13_s32; - safe::var> const b = 29_s32; + safe::constrained_number, TypeParam> const a = 13_s32; + safe::constrained_number, TypeParam> const b = 29_s32; auto const result = a + b; EXPECT_EQ(result.unsafe_value(), 42); } TYPED_TEST(safe_var_ops_test, minus_op) { - safe::var> const a = 32_s32; - safe::var> const b = 8_s32; + safe::constrained_number, TypeParam> const a = 32_s32; + safe::constrained_number, TypeParam> const b = 8_s32; auto const result = a - b; EXPECT_EQ(result.unsafe_value(), 24); } TYPED_TEST(safe_var_ops_test, multiply_op) { - safe::var> const a = 6_s32; - safe::var> const b = 8_s32; + safe::constrained_number, TypeParam> const a = 6_s32; + safe::constrained_number, TypeParam> const b = 8_s32; auto const result = a * b; EXPECT_EQ(result.unsafe_value(), 48); } TYPED_TEST(safe_var_ops_test, divide_op) { - safe::var> const a = 45_s32; - safe::var> const b = 5_s32; + safe::constrained_number, TypeParam> const a = 45_s32; + safe::constrained_number, TypeParam> const b = 5_s32; auto const result = a / b; EXPECT_EQ(result.unsafe_value(), 9); } // FIXME: MODULO NEEDS TO BE REWRITTEN // TYPED_TEST(safe_var_ops_test, modulo_op) { -// safe::var> const a = 12_s32; -// safe::var> const b = 5_s32; +// safe::constrained_number, TypeParam> const a = 12_s32; +// safe::constrained_number, TypeParam> const b = 5_s32; // auto const result = a % b; // EXPECT_EQ(result.unsafe_value(), 2); //} // FIXME: big_integer needs variable left shift support // TYPED_TEST(safe_var_ops_test, left_shift_op) { -// safe::var> const a = 8_s32; -// safe::var> const b = 2_s32; +// safe::constrained_number, TypeParam> const a = 8_s32; +// safe::constrained_number, TypeParam> const b = 2_s32; // auto const result = a << b; // EXPECT_EQ(result.unsafe_value(), 32); //} // FIXME: big_integer needs variable right shift support // TYPED_TEST(safe_var_ops_test, right_shift_op) { -// safe::var> const a = 48_s32; -// safe::var> const b = 2_s32; +// safe::constrained_number, TypeParam> const a = 48_s32; +// safe::constrained_number, TypeParam> const b = 2_s32; // auto const result = a >> b; // EXPECT_EQ(result.unsafe_value(), 12); //} TYPED_TEST(safe_var_ops_test, spaceship_op) { - safe::var> const a = 45_s32; - safe::var> const b = 87_s32; + safe::constrained_number, TypeParam> const a = 45_s32; + safe::constrained_number, TypeParam> const b = 87_s32; EXPECT_TRUE(a == a); EXPECT_TRUE(b == b); @@ -116,22 +116,22 @@ TYPED_TEST(safe_var_ops_test, spaceship_op) { } TYPED_TEST(safe_var_ops_test, max_op) { - safe::var> const a = 48_s32; - safe::var> const b = 32_s32; + safe::constrained_number, TypeParam> const a = 48_s32; + safe::constrained_number, TypeParam> const b = 32_s32; auto const result = std::max(a, b); EXPECT_EQ(result.unsafe_value(), 48); } TYPED_TEST(safe_var_ops_test, min_op) { - safe::var> const a = 48_s32; - safe::var> const b = 32_s32; + safe::constrained_number, TypeParam> const a = 48_s32; + safe::constrained_number, TypeParam> const b = 32_s32; auto const result = std::min(a, b); EXPECT_EQ(result.unsafe_value(), 32); } TYPED_TEST(safe_var_ops_test, clamp_op) { - safe::var> const min = 32_s32; - safe::var> const max = 48_s32; + safe::constrained_number, TypeParam> const min = 32_s32; + safe::constrained_number, TypeParam> const max = 48_s32; auto const result = clamp(65, min, max); EXPECT_EQ(result.unsafe_value(), 48); } @@ -179,24 +179,24 @@ TEST(safe_var_test, abs_op) { } TEST(safe_var_test, bitwise_or_op) { - safe::var> const a = 15_s32; - safe::var> const b = 9_s32; + safe::constrained_number, uint32_t> const a = 15_s32; + safe::constrained_number, uint32_t> const b = 9_s32; auto const result = a & b; EXPECT_EQ(result.unsafe_value(), 9); } TEST(safe_var_test, add_int_const) { - safe::var> const a = 15_s32; + safe::constrained_number, uint32_t> const a = 15_s32; auto const result = a + std::integral_constant{}; EXPECT_EQ(result.unsafe_value(), 25); } -// FIXME: need to automatically convert integral_constants to safe::var +// FIXME: need to automatically convert integral_constants to safe::constrained_number TEST(safe_var_test, use_case_bitfield_extract_1) { safe::u32 const reg = u32_<0xba5eba11>; auto const field = (reg >> u32_<16>)&u32_<0xff>; - EXPECT_TRUE(field.requirement <= mask<0xff>); + EXPECT_TRUE(field.constraint <= constrain_mask<0xff>); EXPECT_EQ(field.unsafe_value(), 0x5e); } @@ -204,6 +204,6 @@ TEST(safe_var_test, use_case_bitfield_extract_2) { auto const reg = u32_<0xba5eba11>; auto const field = (reg >> u32_<16>)&u32_<0xff>; - EXPECT_TRUE(field.requirement <= mask<0xff>); + EXPECT_TRUE(field.constraint <= constrain_mask<0xff>); EXPECT_EQ(field.unsafe_value(), 0x5e); } diff --git a/test/safe/var/CMakeLists.txt b/test/safe/constrained_number/CMakeLists.txt similarity index 100% rename from test/safe/var/CMakeLists.txt rename to test/safe/constrained_number/CMakeLists.txt diff --git a/test/safe/var/assign_constant_max_violation.cpp b/test/safe/constrained_number/assign_constant_max_violation.cpp similarity index 100% rename from test/safe/var/assign_constant_max_violation.cpp rename to test/safe/constrained_number/assign_constant_max_violation.cpp diff --git a/test/safe/var/assign_constant_min_violation.cpp b/test/safe/constrained_number/assign_constant_min_violation.cpp similarity index 100% rename from test/safe/var/assign_constant_min_violation.cpp rename to test/safe/constrained_number/assign_constant_min_violation.cpp diff --git a/test/safe/constrained_number/assign_lhs_rhs_union.cpp b/test/safe/constrained_number/assign_lhs_rhs_union.cpp new file mode 100644 index 0000000..c1ab693 --- /dev/null +++ b/test/safe/constrained_number/assign_lhs_rhs_union.cpp @@ -0,0 +1,10 @@ +#include + +using namespace safe::literals; +using safe::constrain_interval; + +void test() { + safe::constrained_number || constrain_interval<20, 30>, int> a = 0_s32; + safe::constrained_number || constrain_interval<21, 29>, int> b = 11_s32; + a = b; +} diff --git a/test/safe/constrained_number/assign_lhs_union.cpp b/test/safe/constrained_number/assign_lhs_union.cpp new file mode 100644 index 0000000..e3e5e0f --- /dev/null +++ b/test/safe/constrained_number/assign_lhs_union.cpp @@ -0,0 +1,10 @@ +#include + +using namespace safe::literals; +using safe::constrain_interval; + +void test() { + safe::constrained_number || constrain_interval<20, 30>, int> a = 0_s32; + safe::constrained_number, int> b = 11_s32; + a = b; +} diff --git a/test/safe/constrained_number/assign_rhs_union.cpp b/test/safe/constrained_number/assign_rhs_union.cpp new file mode 100644 index 0000000..a54b576 --- /dev/null +++ b/test/safe/constrained_number/assign_rhs_union.cpp @@ -0,0 +1,10 @@ +#include + +using namespace safe::literals; +using safe::constrain_interval; + +void test() { + safe::constrained_number, int> a = 1_s32; + safe::constrained_number || constrain_interval<21, 29>, int> b = 0_s32; + a = b; +} diff --git a/test/safe/var/construct_constant_max_violation.cpp b/test/safe/constrained_number/construct_constant_max_violation.cpp similarity index 100% rename from test/safe/var/construct_constant_max_violation.cpp rename to test/safe/constrained_number/construct_constant_max_violation.cpp diff --git a/test/safe/var/construct_constant_min_violation.cpp b/test/safe/constrained_number/construct_constant_min_violation.cpp similarity index 100% rename from test/safe/var/construct_constant_min_violation.cpp rename to test/safe/constrained_number/construct_constant_min_violation.cpp diff --git a/test/safe/var/interval_larger_than_type.cpp b/test/safe/constrained_number/interval_larger_than_type.cpp similarity index 100% rename from test/safe/var/interval_larger_than_type.cpp rename to test/safe/constrained_number/interval_larger_than_type.cpp diff --git a/test/safe/dsl.cpp b/test/safe/dsl.cpp index c84c04d..6f931d8 100644 --- a/test/safe/dsl.cpp +++ b/test/safe/dsl.cpp @@ -20,11 +20,11 @@ template struct test_interval { template -void check_interval(ival_t lhs, ival_t rhs, +void check_interval(constrain_interval_t lhs, constrain_interval_t rhs, OperationT op) { auto const actual = safe::dsl::detail::simp(op(lhs, rhs)); - for (T i = lhs_min; i <= lhs_max; i = unsafe_cast(i + 1)) { - for (T j = rhs_min; j <= rhs_max; j = unsafe_cast(j + 1)) { + for (T i = lhs_min; i <= lhs_max; i = constraint_cast(i + 1)) { + for (T j = rhs_min; j <= rhs_max; j = constraint_cast(j + 1)) { auto const v = op(i, j); EXPECT_GE(v, actual.min); EXPECT_LE(v, actual.max); @@ -102,11 +102,11 @@ template struct multiply_test_op { template using operands = - mp_list, ival_t<-4 * scale, -2 * scale>, - ival_t<-3 * scale, -1 * scale>, ival_t<-2 * scale, 0 * scale>, - ival_t<-1 * scale, 1 * scale>, ival_t<0 * scale, 2 * scale>, - ival_t<1 * scale, 3 * scale>, ival_t<2 * scale, 4 * scale>, - ival_t<3 * scale, 5 * scale>>; + mp_list, constrain_interval_t<-4 * scale, -2 * scale>, + constrain_interval_t<-3 * scale, -1 * scale>, constrain_interval_t<-2 * scale, 0 * scale>, + constrain_interval_t<-1 * scale, 1 * scale>, constrain_interval_t<0 * scale, 2 * scale>, + constrain_interval_t<1 * scale, 3 * scale>, constrain_interval_t<2 * scale, 4 * scale>, + constrain_interval_t<3 * scale, 5 * scale>>; TEST(safe_dsl_test, add_op) { test_operation, operands<5>>(); @@ -130,19 +130,19 @@ TEST(safe_dsl_test, divide_op) { // }; TEST(safe_dsl_test, modulo_op) { - EXPECT_EQ((ival<101, 1000> % ival<1, 100>), (ival<0, 99>)); + EXPECT_EQ((constrain_interval<101, 1000> % constrain_interval<1, 100>), (constrain_interval<0, 99>)); - EXPECT_EQ((ival<100, 1000> % ival<100, 1000>), - (ival<0, 999> || ival<100, 999>)); + EXPECT_EQ((constrain_interval<100, 1000> % constrain_interval<100, 1000>), + (constrain_interval<0, 999> || constrain_interval<100, 999>)); - EXPECT_EQ((ival<100, 1000> % ival<2000, 3000>), (ival<100, 1000>)); + EXPECT_EQ((constrain_interval<100, 1000> % constrain_interval<2000, 3000>), (constrain_interval<100, 1000>)); - EXPECT_EQ((ival<-1000, -100> % ival<2000, 3000>), (ival<-1000, -100>)); + EXPECT_EQ((constrain_interval<-1000, -100> % constrain_interval<2000, 3000>), (constrain_interval<-1000, -100>)); - EXPECT_EQ((ival<-1000, -100> % ival<100, 1000>), - (ival<-900, 0> || ival<-999, -100>)); + EXPECT_EQ((constrain_interval<-1000, -100> % constrain_interval<100, 1000>), + (constrain_interval<-900, 0> || constrain_interval<-999, -100>)); - EXPECT_EQ((ival<-1000, -101> % ival<1, 100>), (ival<-99, 0>)); + EXPECT_EQ((constrain_interval<-1000, -101> % constrain_interval<1, 100>), (constrain_interval<-99, 0>)); // test_operation< // modulo_test_op, // operands<5>, @@ -162,123 +162,123 @@ TEST(safe_dsl_test, modulo_op) { // >(); // EXPECT_EQ( - // (ival<0, 1000> % ival<100, 100>), - // (ival<0l, 99l>) + // (constrain_interval<0, 1000> % constrain_interval<100, 100>), + // (constrain_interval<0l, 99l>) // ); // EXPECT_EQ( - // (ival<0, 50> % ival<100, 100>), - // (ival<0l, 50l>) + // (constrain_interval<0, 50> % constrain_interval<100, 100>), + // (constrain_interval<0l, 50l>) // ); // EXPECT_EQ( - // (ival<0, 300> % ival<1000, 1000>), - // (ival<0l, 300l>) + // (constrain_interval<0, 300> % constrain_interval<1000, 1000>), + // (constrain_interval<0l, 300l>) // ); // EXPECT_EQ( - // (ival<0, 100> % ival<1, 100>), - // (ival<0l, 99l>) + // (constrain_interval<0, 100> % constrain_interval<1, 100>), + // (constrain_interval<0l, 99l>) // ); // EXPECT_EQ( - // (ival<-100, 1> % ival<1, 100>), - // (ival<-99l, 1l>) + // (constrain_interval<-100, 1> % constrain_interval<1, 100>), + // (constrain_interval<-99l, 1l>) // ); // EXPECT_EQ( - // (ival<-100, 100> % ival<1, 100>), - // (ival<-99l, 99l>) + // (constrain_interval<-100, 100> % constrain_interval<1, 100>), + // (constrain_interval<-99l, 99l>) // ); // EXPECT_EQ( - // (ival<-100, 100> % ival<1, 1000>), - // (ival<-100l, 100l>) + // (constrain_interval<-100, 100> % constrain_interval<1, 1000>), + // (constrain_interval<-100l, 100l>) // ); // EXPECT_EQ( - // (ival<0, 50> % ival<1, 100>), - // (ival<0l, 50l>) + // (constrain_interval<0, 50> % constrain_interval<1, 100>), + // (constrain_interval<0l, 50l>) // ); // EXPECT_EQ( - // (ival<0, 100> % ival<1, 50>), - // (ival<0l, 49l>) + // (constrain_interval<0, 100> % constrain_interval<1, 50>), + // (constrain_interval<0l, 49l>) // ); } TEST(safe_dsl_test, ival_union_simplification) { - EXPECT_EQ((ival<20, 30> || ival<0, 10>), (ival<0, 10> || ival<20, 30>)); + EXPECT_EQ((constrain_interval<20, 30> || constrain_interval<0, 10>), (constrain_interval<0, 10> || constrain_interval<20, 30>)); - EXPECT_EQ((ival<0, 10> || ival<10, 30>), (ival<0, 30>)); + EXPECT_EQ((constrain_interval<0, 10> || constrain_interval<10, 30>), (constrain_interval<0, 30>)); - EXPECT_EQ((ival<0, 10> || ival<11, 30>), (ival<0, 30>)); + EXPECT_EQ((constrain_interval<0, 10> || constrain_interval<11, 30>), (constrain_interval<0, 30>)); - EXPECT_NE((ival<0, 10> || ival<12, 30>), (ival<0, 30>)); + EXPECT_NE((constrain_interval<0, 10> || constrain_interval<12, 30>), (constrain_interval<0, 30>)); } TEST(safe_dsl_test, set_union_simplification) { - EXPECT_EQ((set<0, 1, 2, 3, 4>), (ival<0, 4>)); + EXPECT_EQ((constrain_set<0, 1, 2, 3, 4>), (constrain_interval<0, 4>)); - EXPECT_EQ((set<0, 1, 2, 4, 5, 6>), (ival<0, 2> || ival<4, 6>)); + EXPECT_EQ((constrain_set<0, 1, 2, 4, 5, 6>), (constrain_interval<0, 2> || constrain_interval<4, 6>)); - EXPECT_EQ((set<0, 1, 2, 4, 5, 6> || ival<1, 7>), (ival<0, 7>)); + EXPECT_EQ((constrain_set<0, 1, 2, 4, 5, 6> || constrain_interval<1, 7>), (constrain_interval<0, 7>)); } TEST(safe_dsl_test, subtraction_union_simplification) { - EXPECT_EQ((ival<0, 10> - ival<20, 30>), (ival<-30, -10>)); + EXPECT_EQ((constrain_interval<0, 10> - constrain_interval<20, 30>), (constrain_interval<-30, -10>)); - EXPECT_EQ(((ival<0, 10> || ival<40, 80>)-ival<20, 30>), - (ival<-30, -10> || ival<10, 60>)); + EXPECT_EQ(((constrain_interval<0, 10> || constrain_interval<40, 80>)-constrain_interval<20, 30>), + (constrain_interval<-30, -10> || constrain_interval<10, 60>)); - EXPECT_EQ((ival<20, 30> - (ival<0, 10> || ival<40, 80>)), - (ival<10, 30> || ival<-60, -10>)); + EXPECT_EQ((constrain_interval<20, 30> - (constrain_interval<0, 10> || constrain_interval<40, 80>)), + (constrain_interval<10, 30> || constrain_interval<-60, -10>)); EXPECT_EQ( - ((ival<0, 10> || ival<100, 200>)-(ival<20, 30> || ival<400, 800>)), - (ival<-800, -200> || ival<-30, -10> || ival<70, 180>)); + ((constrain_interval<0, 10> || constrain_interval<100, 200>)-(constrain_interval<20, 30> || constrain_interval<400, 800>)), + (constrain_interval<-800, -200> || constrain_interval<-30, -10> || constrain_interval<70, 180>)); } TEST(safe_dsl_test, multiplication_union_simplification) { - EXPECT_EQ((ival<0, 10> * ival<20, 30>), (ival<0, 300>)); + EXPECT_EQ((constrain_interval<0, 10> * constrain_interval<20, 30>), (constrain_interval<0, 300>)); - EXPECT_EQ(((ival<0, 10> || ival<40, 80>)*ival<20, 30>), - (ival<0, 300> || ival<800, 2400>)); + EXPECT_EQ(((constrain_interval<0, 10> || constrain_interval<40, 80>)*constrain_interval<20, 30>), + (constrain_interval<0, 300> || constrain_interval<800, 2400>)); - EXPECT_EQ((ival<20, 30> * (ival<0, 10> || ival<40, 80>)), - (ival<0, 300> || ival<800, 2400>)); + EXPECT_EQ((constrain_interval<20, 30> * (constrain_interval<0, 10> || constrain_interval<40, 80>)), + (constrain_interval<0, 300> || constrain_interval<800, 2400>)); EXPECT_EQ( - ((ival<0, 10> || ival<100, 200>)*(ival<20, 30> || ival<400, 800>)), - (ival<0, 8000> || ival<40000, 160000>)); + ((constrain_interval<0, 10> || constrain_interval<100, 200>)*(constrain_interval<20, 30> || constrain_interval<400, 800>)), + (constrain_interval<0, 8000> || constrain_interval<40000, 160000>)); } TEST(safe_dsl_test, shift_left_op) { - EXPECT_EQ((ival<0, 10> << ival<0, 10>), (ival<0, 10240>)); + EXPECT_EQ((constrain_interval<0, 10> << constrain_interval<0, 10>), (constrain_interval<0, 10240>)); } TEST(safe_dsl_test, complicated_expression_simplification) { - EXPECT_EQ(((ival<0, 10> || (ival<100, 200> + set<2, 7, 9, 42>) || - ival<300, 600>)*(ival<20, 30> || ival<400, 800>)), - (ival<0, 18000> || ival<40800, 480000>)); + EXPECT_EQ(((constrain_interval<0, 10> || (constrain_interval<100, 200> + constrain_set<2, 7, 9, 42>) || + constrain_interval<300, 600>)*(constrain_interval<20, 30> || constrain_interval<400, 800>)), + (constrain_interval<0, 18000> || constrain_interval<40800, 480000>)); } TEST(safe_dsl_test, mask_shift_left) { - EXPECT_EQ((mask<0b0> << ival<0, 0>), mask<0b0>); - EXPECT_EQ((mask<0b1> << ival<1, 1>), mask<0b10>); + EXPECT_EQ((constrain_mask<0b0> << constrain_interval<0, 0>), constrain_mask<0b0>); + EXPECT_EQ((constrain_mask<0b1> << constrain_interval<1, 1>), constrain_mask<0b10>); - EXPECT_EQ((mask<0b1> << ival<0, 1>), mask<0b11>); - EXPECT_EQ((mask<0b1> << ival<0, 4>), mask<0b11111>); - EXPECT_EQ((mask<0b1> << ival<4, 8>), mask<0b111110000>); + EXPECT_EQ((constrain_mask<0b1> << constrain_interval<0, 1>), constrain_mask<0b11>); + EXPECT_EQ((constrain_mask<0b1> << constrain_interval<0, 4>), constrain_mask<0b11111>); + EXPECT_EQ((constrain_mask<0b1> << constrain_interval<4, 8>), constrain_mask<0b111110000>); - EXPECT_EQ((mask<0b1000001> << ival<0, 1>), mask<0b11000011>); - EXPECT_EQ((mask<0b1000001> << ival<2, 4>), mask<0b11100011100>); + EXPECT_EQ((constrain_mask<0b1000001> << constrain_interval<0, 1>), constrain_mask<0b11000011>); + EXPECT_EQ((constrain_mask<0b1000001> << constrain_interval<2, 4>), constrain_mask<0b11100011100>); } TEST(safe_dsl_test, mask_shift_right) { - EXPECT_EQ((mask<0b1000> >> ival<0, 0>), mask<0b1000>); - EXPECT_EQ((mask<0b1000> >> ival<1, 1>), mask<0b0100>); + EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<0, 0>), constrain_mask<0b1000>); + EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<1, 1>), constrain_mask<0b0100>); - EXPECT_EQ((mask<0b1000> >> ival<0, 1>), mask<0b1100>); - EXPECT_EQ((mask<0b10000000> >> ival<4, 6>), mask<0b1110>); + EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<0, 1>), constrain_mask<0b1100>); + EXPECT_EQ((constrain_mask<0b10000000> >> constrain_interval<4, 6>), constrain_mask<0b1110>); } diff --git a/test/safe/dsl/abs.cpp b/test/safe/dsl/abs.cpp index 8a7f125..03d97d2 100644 --- a/test/safe/dsl/abs.cpp +++ b/test/safe/dsl/abs.cpp @@ -11,21 +11,21 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_abs, ival_identity) { - EXPECT_EQ(abs(ival<10, 20>), (ival<10, 20>)); + EXPECT_EQ(abs(constrain_interval<10, 20>), (constrain_interval<10, 20>)); } TEST(safe_dsl_abs, ival_all_negative) { - EXPECT_EQ(abs(ival<-30, -10>), (ival<10, 30>)); + EXPECT_EQ(abs(constrain_interval<-30, -10>), (constrain_interval<10, 30>)); } TEST(safe_dsl_abs, ival_straddle_min) { - EXPECT_EQ(abs(ival<-30, 10>), (ival<0, 30>)); + EXPECT_EQ(abs(constrain_interval<-30, 10>), (constrain_interval<0, 30>)); } TEST(safe_dsl_abs, ival_straddle_max) { - EXPECT_EQ(abs(ival<-30, 40>), (ival<0, 40>)); + EXPECT_EQ(abs(constrain_interval<-30, 40>), (constrain_interval<0, 40>)); } TEST(safe_dsl_abs, ival_straddle_eq) { - EXPECT_EQ(abs(ival<-50, 50>), (ival<0, 50>)); + EXPECT_EQ(abs(constrain_interval<-50, 50>), (constrain_interval<0, 50>)); } diff --git a/test/safe/dsl/add.cpp b/test/safe/dsl/add.cpp index ffe25dc..ff54cdc 100644 --- a/test/safe/dsl/add.cpp +++ b/test/safe/dsl/add.cpp @@ -13,56 +13,56 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_add, add_two_ival_constants) { - EXPECT_EQ((ival<30, 30> + ival<12, 12>), (ival<42, 42>)); + EXPECT_EQ((constrain_interval<30, 30> + constrain_interval<12, 12>), (constrain_interval<42, 42>)); } TEST(safe_dsl_add, add_three_ival_constants) { - EXPECT_EQ((ival<30, 30> + ival<12, 12> + ival<8, 8>), (ival<50, 50>)); + EXPECT_EQ((constrain_interval<30, 30> + constrain_interval<12, 12> + constrain_interval<8, 8>), (constrain_interval<50, 50>)); } TEST(safe_dsl_add, add_two_intervals) { - EXPECT_EQ((ival<10, 20> + ival<40, 80>), (ival<50, 100>)); + EXPECT_EQ((constrain_interval<10, 20> + constrain_interval<40, 80>), (constrain_interval<50, 100>)); } TEST(safe_dsl_add, add_three_intervals) { - EXPECT_EQ((ival<10, 20> + ival<40, 80> + ival<1, 5>), (ival<51, 105>)); + EXPECT_EQ((constrain_interval<10, 20> + constrain_interval<40, 80> + constrain_interval<1, 5>), (constrain_interval<51, 105>)); } TEST(safe_dsl_add, add_four_intervals) { - EXPECT_EQ((ival<10, 20> + ival<40, 80> + ival<1, 5> + ival<3, 7>), - (ival<54, 112>)); + EXPECT_EQ((constrain_interval<10, 20> + constrain_interval<40, 80> + constrain_interval<1, 5> + constrain_interval<3, 7>), + (constrain_interval<54, 112>)); } TEST(safe_dsl_add, add_two_mask_constants) { - EXPECT_EQ((mask<0, 12> + mask<0, 8>), (ival<20, 20>)); + EXPECT_EQ((constrain_mask<0, 12> + constrain_mask<0, 8>), (constrain_interval<20, 20>)); } TEST(safe_dsl_add, add_two_masks_1) { - auto const actual = dsl::detail::simp(mask<0x3> + mask<0x3>); + auto const actual = dsl::detail::simp(constrain_mask<0x3> + constrain_mask<0x3>); EXPECT_EQ(actual.var_bits, 0x7); } TEST(safe_dsl_add, add_two_masks_2) { - EXPECT_EQ((mask<0xf> + mask<0xff>), (mask<0x1ff>)); + EXPECT_EQ((constrain_mask<0xf> + constrain_mask<0xff>), (constrain_mask<0x1ff>)); } TEST(safe_dsl_add, add_two_masks_3) { - EXPECT_EQ((mask<0, 13> + mask<0, 29>), (mask<0, 42>)); + EXPECT_EQ((constrain_mask<0, 13> + constrain_mask<0, 29>), (constrain_mask<0, 42>)); } TEST(safe_dsl_add, add_two_masks_4) { - EXPECT_EQ((mask<0x11> + mask<0x11>), (mask<0x33>)); + EXPECT_EQ((constrain_mask<0x11> + constrain_mask<0x11>), (constrain_mask<0x33>)); } TEST(safe_dsl_add, union_simplification) { - EXPECT_EQ(((ival<0, 10> || ival<40, 80>)+ival<20, 30>), - (ival<20, 40> || ival<60, 110>)); + EXPECT_EQ(((constrain_interval<0, 10> || constrain_interval<40, 80>)+constrain_interval<20, 30>), + (constrain_interval<20, 40> || constrain_interval<60, 110>)); - EXPECT_EQ((ival<20, 30> + (ival<0, 10> || ival<40, 80>)), - (ival<20, 40> || ival<60, 110>)); + EXPECT_EQ((constrain_interval<20, 30> + (constrain_interval<0, 10> || constrain_interval<40, 80>)), + (constrain_interval<20, 40> || constrain_interval<60, 110>)); EXPECT_EQ( - ((ival<0, 10> || ival<100, 200>)+(ival<20, 30> || ival<400, 800>)), - (ival<20, 40> || ival<120, 230> || ival<400, 1000>)); + ((constrain_interval<0, 10> || constrain_interval<100, 200>)+(constrain_interval<20, 30> || constrain_interval<400, 800>)), + (constrain_interval<20, 40> || constrain_interval<120, 230> || constrain_interval<400, 1000>)); } diff --git a/test/safe/dsl/bitwise_and.cpp b/test/safe/dsl/bitwise_and.cpp index 7fea5ac..e9e76d9 100644 --- a/test/safe/dsl/bitwise_and.cpp +++ b/test/safe/dsl/bitwise_and.cpp @@ -11,43 +11,43 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_bitwise_and, mask_variable_bits_only) { - EXPECT_EQ(mask<0b0> & mask<0b0>, mask<0b0>); - EXPECT_EQ(mask<0b1> & mask<0b0>, mask<0b0>); - EXPECT_EQ(mask<0b0> & mask<0b1>, mask<0b0>); - EXPECT_EQ(mask<0b1> & mask<0b1>, mask<0b1>); - - EXPECT_EQ(mask<0b01> & mask<0b10>, mask<0b00>); - EXPECT_EQ(mask<0b01> & mask<0b11>, mask<0b01>); - EXPECT_EQ(mask<0b1110> & mask<0b0111>, mask<0b0110>); + EXPECT_EQ(constrain_mask<0b0> & constrain_mask<0b0>, constrain_mask<0b0>); + EXPECT_EQ(constrain_mask<0b1> & constrain_mask<0b0>, constrain_mask<0b0>); + EXPECT_EQ(constrain_mask<0b0> & constrain_mask<0b1>, constrain_mask<0b0>); + EXPECT_EQ(constrain_mask<0b1> & constrain_mask<0b1>, constrain_mask<0b1>); + + EXPECT_EQ(constrain_mask<0b01> & constrain_mask<0b10>, constrain_mask<0b00>); + EXPECT_EQ(constrain_mask<0b01> & constrain_mask<0b11>, constrain_mask<0b01>); + EXPECT_EQ(constrain_mask<0b1110> & constrain_mask<0b0111>, constrain_mask<0b0110>); } TEST(safe_dsl_bitwise_and, constant_bits_only) { - EXPECT_EQ((mask<0, 0b0> & mask<0, 0b0>), (mask<0, 0b0>)); - EXPECT_EQ((mask<0, 0b1> & mask<0, 0b0>), (mask<0, 0b0>)); - EXPECT_EQ((mask<0, 0b0> & mask<0, 0b1>), (mask<0, 0b0>)); - EXPECT_EQ((mask<0, 0b1> & mask<0, 0b1>), (mask<0, 0b1>)); + EXPECT_EQ((constrain_mask<0, 0b0> & constrain_mask<0, 0b0>), (constrain_mask<0, 0b0>)); + EXPECT_EQ((constrain_mask<0, 0b1> & constrain_mask<0, 0b0>), (constrain_mask<0, 0b0>)); + EXPECT_EQ((constrain_mask<0, 0b0> & constrain_mask<0, 0b1>), (constrain_mask<0, 0b0>)); + EXPECT_EQ((constrain_mask<0, 0b1> & constrain_mask<0, 0b1>), (constrain_mask<0, 0b1>)); - EXPECT_EQ((mask<0, 0xff00> & mask<0, 0xfeed>), (mask<0, 0xfe00>)); + EXPECT_EQ((constrain_mask<0, 0xff00> & constrain_mask<0, 0xfeed>), (constrain_mask<0, 0xfe00>)); } TEST(safe_dsl_bitwise_and, const_x_var_bits) { - EXPECT_EQ((mask<0, 0> & mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 0> & mask<0, 1>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 0> & mask<1, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 0> & mask<1, 1>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 1> & mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 1> & mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 1> & mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<0, 1> & mask<1, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> & mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<1, 0> & mask<0, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> & mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> & mask<1, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> & mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<1, 1> & mask<0, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> & mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> & mask<1, 1>), (mask<1, 0>)); - - EXPECT_EQ((mask<0, 0xff> & mask<0xff>), (mask<0xff>)); - EXPECT_EQ((mask<0xff> & mask<0, 0xff>), (mask<0xff>)); + EXPECT_EQ((constrain_mask<0, 0> & constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 0> & constrain_mask<0, 1>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 0> & constrain_mask<1, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 0> & constrain_mask<1, 1>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 1> & constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 1> & constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 1> & constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<0, 1> & constrain_mask<1, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> & constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<1, 0> & constrain_mask<0, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> & constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> & constrain_mask<1, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> & constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<1, 1> & constrain_mask<0, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> & constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> & constrain_mask<1, 1>), (constrain_mask<1, 0>)); + + EXPECT_EQ((constrain_mask<0, 0xff> & constrain_mask<0xff>), (constrain_mask<0xff>)); + EXPECT_EQ((constrain_mask<0xff> & constrain_mask<0, 0xff>), (constrain_mask<0xff>)); } diff --git a/test/safe/dsl/bitwise_invert.cpp b/test/safe/dsl/bitwise_invert.cpp index e6e7891..e513956 100644 --- a/test/safe/dsl/bitwise_invert.cpp +++ b/test/safe/dsl/bitwise_invert.cpp @@ -11,7 +11,7 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_bitwise_invert, mask_bitwise_invert) { - EXPECT_EQ(~mask<0>, (mask<0, ~0>)); - EXPECT_EQ(~mask<0b1111>, (mask<0b1111, ~0>)); - EXPECT_NE(~mask<0>, mask<0>); + EXPECT_EQ(~constrain_mask<0>, (constrain_mask<0, ~0>)); + EXPECT_EQ(~constrain_mask<0b1111>, (constrain_mask<0b1111, ~0>)); + EXPECT_NE(~constrain_mask<0>, constrain_mask<0>); } \ No newline at end of file diff --git a/test/safe/dsl/bitwise_or.cpp b/test/safe/dsl/bitwise_or.cpp index 8fee1d8..69fa17b 100644 --- a/test/safe/dsl/bitwise_or.cpp +++ b/test/safe/dsl/bitwise_or.cpp @@ -11,43 +11,43 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_bitwise_or, mask_variable_bits_only) { - EXPECT_EQ(mask<0b0> | mask<0b0>, mask<0b0>); - EXPECT_EQ(mask<0b1> | mask<0b0>, mask<0b1>); - EXPECT_EQ(mask<0b0> | mask<0b1>, mask<0b1>); - EXPECT_EQ(mask<0b1> | mask<0b1>, mask<0b1>); + EXPECT_EQ(constrain_mask<0b0> | constrain_mask<0b0>, constrain_mask<0b0>); + EXPECT_EQ(constrain_mask<0b1> | constrain_mask<0b0>, constrain_mask<0b1>); + EXPECT_EQ(constrain_mask<0b0> | constrain_mask<0b1>, constrain_mask<0b1>); + EXPECT_EQ(constrain_mask<0b1> | constrain_mask<0b1>, constrain_mask<0b1>); - EXPECT_EQ(mask<0b01> | mask<0b10>, mask<0b11>); + EXPECT_EQ(constrain_mask<0b01> | constrain_mask<0b10>, constrain_mask<0b11>); - EXPECT_EQ(mask<0xba5e0000> | mask<0x0000ba11>, mask<0xba5eba11>); + EXPECT_EQ(constrain_mask<0xba5e0000> | constrain_mask<0x0000ba11>, constrain_mask<0xba5eba11>); } TEST(safe_dsl_bitwise_or, constant_bits_only) { - EXPECT_EQ((mask<0, 0> | mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 1> | mask<0, 0>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 0> | mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 1> | mask<0, 1>), (mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 0> | constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 1> | constrain_mask<0, 0>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 0> | constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 1> | constrain_mask<0, 1>), (constrain_mask<0, 1>)); - EXPECT_EQ((mask<0, 0xff00> | mask<0, 0xfeed>), (mask<0, 0xffed>)); + EXPECT_EQ((constrain_mask<0, 0xff00> | constrain_mask<0, 0xfeed>), (constrain_mask<0, 0xffed>)); } TEST(safe_dsl_bitwise_or, const_x_var_bits) { - EXPECT_EQ((mask<0, 0> | mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 0> | mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 0> | mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<0, 0> | mask<1, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<0, 1> | mask<0, 0>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 1> | mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 1> | mask<1, 0>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 1> | mask<1, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<1, 0> | mask<0, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> | mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<1, 0> | mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> | mask<1, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> | mask<0, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> | mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<1, 1> | mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> | mask<1, 1>), (mask<1, 0>)); - - EXPECT_EQ((mask<0, 0xff> | mask<0xff>), (mask<0, 0xff>)); - EXPECT_EQ((mask<0xff> | mask<0, 0xff>), (mask<0, 0xff>)); + EXPECT_EQ((constrain_mask<0, 0> | constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 0> | constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 0> | constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<0, 0> | constrain_mask<1, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<0, 1> | constrain_mask<0, 0>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 1> | constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 1> | constrain_mask<1, 0>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 1> | constrain_mask<1, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<1, 0> | constrain_mask<0, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> | constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<1, 0> | constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> | constrain_mask<1, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> | constrain_mask<0, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> | constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<1, 1> | constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> | constrain_mask<1, 1>), (constrain_mask<1, 0>)); + + EXPECT_EQ((constrain_mask<0, 0xff> | constrain_mask<0xff>), (constrain_mask<0, 0xff>)); + EXPECT_EQ((constrain_mask<0xff> | constrain_mask<0, 0xff>), (constrain_mask<0, 0xff>)); } \ No newline at end of file diff --git a/test/safe/dsl/bitwise_xor.cpp b/test/safe/dsl/bitwise_xor.cpp index 5c04fe8..46d3880 100644 --- a/test/safe/dsl/bitwise_xor.cpp +++ b/test/safe/dsl/bitwise_xor.cpp @@ -11,43 +11,43 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_bitwise_xor, mask_variable_bits_only) { - EXPECT_EQ(mask<0> ^ mask<0>, mask<0>); - EXPECT_EQ(mask<1> ^ mask<0>, mask<1>); - EXPECT_EQ(mask<0> ^ mask<1>, mask<1>); - EXPECT_EQ(mask<1> ^ mask<1>, mask<1>); + EXPECT_EQ(constrain_mask<0> ^ constrain_mask<0>, constrain_mask<0>); + EXPECT_EQ(constrain_mask<1> ^ constrain_mask<0>, constrain_mask<1>); + EXPECT_EQ(constrain_mask<0> ^ constrain_mask<1>, constrain_mask<1>); + EXPECT_EQ(constrain_mask<1> ^ constrain_mask<1>, constrain_mask<1>); - EXPECT_EQ(mask<0b01> ^ mask<0b10>, mask<0b11>); + EXPECT_EQ(constrain_mask<0b01> ^ constrain_mask<0b10>, constrain_mask<0b11>); - EXPECT_EQ(mask<0xba5e0000> ^ mask<0x0000ba11>, mask<0xba5eba11>); + EXPECT_EQ(constrain_mask<0xba5e0000> ^ constrain_mask<0x0000ba11>, constrain_mask<0xba5eba11>); } TEST(safe_dsl_bitwise_xor, constant_bits_only) { - EXPECT_EQ((mask<0, 0> ^ mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 1> ^ mask<0, 0>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 0> ^ mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 1> ^ mask<0, 1>), (mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 0> ^ constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 1> ^ constrain_mask<0, 0>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 0> ^ constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 1> ^ constrain_mask<0, 1>), (constrain_mask<0, 0>)); - EXPECT_EQ((mask<0, 0xff00> ^ mask<0, 0xfeed>), (mask<0, 0x01ed>)); + EXPECT_EQ((constrain_mask<0, 0xff00> ^ constrain_mask<0, 0xfeed>), (constrain_mask<0, 0x01ed>)); } TEST(safe_dsl_bitwise_xor, const_x_var_bits) { - EXPECT_EQ((mask<0, 0> ^ mask<0, 0>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 0> ^ mask<0, 1>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 0> ^ mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<0, 0> ^ mask<1, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<0, 1> ^ mask<0, 0>), (mask<0, 1>)); - EXPECT_EQ((mask<0, 1> ^ mask<0, 1>), (mask<0, 0>)); - EXPECT_EQ((mask<0, 1> ^ mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<0, 1> ^ mask<1, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> ^ mask<0, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> ^ mask<0, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> ^ mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 0> ^ mask<1, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> ^ mask<0, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> ^ mask<0, 1>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> ^ mask<1, 0>), (mask<1, 0>)); - EXPECT_EQ((mask<1, 1> ^ mask<1, 1>), (mask<1, 0>)); - - EXPECT_EQ((mask<0, 0xff> ^ mask<0xff>), (mask<0xff>)); - EXPECT_EQ((mask<0xff> ^ mask<0, 0xff>), (mask<0xff>)); + EXPECT_EQ((constrain_mask<0, 0> ^ constrain_mask<0, 0>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 0> ^ constrain_mask<0, 1>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 0> ^ constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<0, 0> ^ constrain_mask<1, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<0, 1> ^ constrain_mask<0, 0>), (constrain_mask<0, 1>)); + EXPECT_EQ((constrain_mask<0, 1> ^ constrain_mask<0, 1>), (constrain_mask<0, 0>)); + EXPECT_EQ((constrain_mask<0, 1> ^ constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<0, 1> ^ constrain_mask<1, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> ^ constrain_mask<0, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> ^ constrain_mask<0, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> ^ constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 0> ^ constrain_mask<1, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> ^ constrain_mask<0, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> ^ constrain_mask<0, 1>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> ^ constrain_mask<1, 0>), (constrain_mask<1, 0>)); + EXPECT_EQ((constrain_mask<1, 1> ^ constrain_mask<1, 1>), (constrain_mask<1, 0>)); + + EXPECT_EQ((constrain_mask<0, 0xff> ^ constrain_mask<0xff>), (constrain_mask<0xff>)); + EXPECT_EQ((constrain_mask<0xff> ^ constrain_mask<0, 0xff>), (constrain_mask<0xff>)); } \ No newline at end of file diff --git a/test/safe/dsl/constrain_mask.cpp b/test/safe/dsl/constrain_mask.cpp new file mode 100644 index 0000000..29fcc54 --- /dev/null +++ b/test/safe/dsl/constrain_mask.cpp @@ -0,0 +1,190 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; + +using namespace safe; +using namespace safe::literals; + +TEST(safe_dsl_mask, create_mask) { [[maybe_unused]] auto v = constrain_mask<0b1001>; } + +TEST(safe_dsl_mask, mask_subset_var_only) { + EXPECT_TRUE(constrain_mask<0b1111> >= constrain_mask<0b1010>); + EXPECT_TRUE(constrain_mask<0b1111> >= constrain_mask<0b0000>); + EXPECT_TRUE(constrain_mask<0b1111> >= constrain_mask<0b1111>); + + EXPECT_FALSE(constrain_mask<0b1001> >= constrain_mask<0b1010>); + EXPECT_FALSE(constrain_mask<0b0001> >= constrain_mask<0b0010>); + EXPECT_FALSE(constrain_mask<0b0001> >= constrain_mask<0b0011>); +} + +TEST(safe_dsl_mask, mask_subset_const_only) { + EXPECT_TRUE((constrain_mask<0, 0b1111>) >= (constrain_mask<0, 0b1111>)); + + EXPECT_FALSE((constrain_mask<0, 0b1111>) >= (constrain_mask<0, 0b0000>)); + EXPECT_FALSE((constrain_mask<0, 0b1111>) >= (constrain_mask<0, 0b1010>)); + EXPECT_FALSE((constrain_mask<0, 0b1001>) >= (constrain_mask<0, 0b1010>)); + EXPECT_FALSE((constrain_mask<0, 0b0001>) >= (constrain_mask<0, 0b0010>)); + EXPECT_FALSE((constrain_mask<0, 0b0001>) >= (constrain_mask<0, 0b0011>)); +} + +TEST(safe_dsl_mask, mask_superset_var_only) { + EXPECT_TRUE(constrain_mask<0b1000> <= constrain_mask<0b1100>); + EXPECT_TRUE(constrain_mask<0b0000> <= constrain_mask<0b0000>); + EXPECT_TRUE(constrain_mask<0b0001> <= constrain_mask<0b1011>); + + EXPECT_FALSE(constrain_mask<0b1001> <= constrain_mask<0b1100>); + EXPECT_FALSE(constrain_mask<0b0010> <= constrain_mask<0b0000>); + EXPECT_FALSE(constrain_mask<0b0100> <= constrain_mask<0b1011>); +} + +TEST(safe_dsl_mask, mask_superset_const_only) { + EXPECT_TRUE((constrain_mask<0, 0b1111>) <= (constrain_mask<0, 0b1111>)); + + EXPECT_FALSE((constrain_mask<0, 0b1111>) <= (constrain_mask<0, 0b0000>)); + EXPECT_FALSE((constrain_mask<0, 0b1111>) <= (constrain_mask<0, 0b1010>)); + EXPECT_FALSE((constrain_mask<0, 0b1001>) <= (constrain_mask<0, 0b1010>)); + EXPECT_FALSE((constrain_mask<0, 0b0001>) <= (constrain_mask<0, 0b0010>)); + EXPECT_FALSE((constrain_mask<0, 0b0001>) <= (constrain_mask<0, 0b0011>)); +} + +TEST(safe_dsl_mask, mask_equal_var_only) { + EXPECT_TRUE(constrain_mask<0b1000> == constrain_mask<0b1000>); + EXPECT_TRUE(constrain_mask<0b0000> == constrain_mask<0b0000>); + EXPECT_TRUE(constrain_mask<0b0001> == constrain_mask<0b0001>); + + EXPECT_FALSE(constrain_mask<0b1000> == constrain_mask<0b1001>); + EXPECT_FALSE(constrain_mask<0b0011> == constrain_mask<0b0010>); + EXPECT_FALSE(constrain_mask<0b0110> == constrain_mask<0b0100>); +} + +TEST(safe_dsl_mask, mask_equal_const_only) { + EXPECT_TRUE((constrain_mask<0, 0b1000>) == (constrain_mask<0, 0b1000>)); + EXPECT_TRUE((constrain_mask<0, 0b0000>) == (constrain_mask<0, 0b0000>)); + EXPECT_TRUE((constrain_mask<0, 0b0001>) == (constrain_mask<0, 0b0001>)); + EXPECT_FALSE((constrain_mask<0, 0b1000>) == (constrain_mask<0, 0b1001>)); + EXPECT_FALSE((constrain_mask<0, 0b0011>) == (constrain_mask<0, 0b0010>)); + EXPECT_FALSE((constrain_mask<0, 0b0110>) == (constrain_mask<0, 0b0100>)); +} + +TEST(safe_dsl_mask, mask_not_equal_var_only) { + EXPECT_FALSE(constrain_mask<0b1000> != constrain_mask<0b1000>); + EXPECT_FALSE(constrain_mask<0b0000> != constrain_mask<0b0000>); + EXPECT_FALSE(constrain_mask<0b0001> != constrain_mask<0b0001>); + + EXPECT_TRUE(constrain_mask<0b1000> != constrain_mask<0b1001>); + EXPECT_TRUE(constrain_mask<0b0011> != constrain_mask<0b0010>); + EXPECT_TRUE(constrain_mask<0b0110> != constrain_mask<0b0100>); +} + +TEST(safe_dsl_mask, mask_not_equal_const_only) { + EXPECT_FALSE((constrain_mask<0, 0b1000>) != (constrain_mask<0, 0b1000>)); + EXPECT_FALSE((constrain_mask<0, 0b0000>) != (constrain_mask<0, 0b0000>)); + EXPECT_FALSE((constrain_mask<0, 0b0001>) != (constrain_mask<0, 0b0001>)); + EXPECT_TRUE((constrain_mask<0, 0b1000>) != (constrain_mask<0, 0b1001>)); + EXPECT_TRUE((constrain_mask<0, 0b0011>) != (constrain_mask<0, 0b0010>)); + EXPECT_TRUE((constrain_mask<0, 0b0110>) != (constrain_mask<0, 0b0100>)); +} + +TEST(safe_dsl_mask, detail_is_basic_mask) { + EXPECT_TRUE(dsl::detail::is_basic_mask(0xffffffff)); + EXPECT_TRUE(dsl::detail::is_basic_mask(0xffffffffu)); + + EXPECT_TRUE(dsl::detail::is_basic_mask(0b1111111111)); + EXPECT_TRUE(dsl::detail::is_basic_mask(0b0000111111)); + EXPECT_TRUE(dsl::detail::is_basic_mask(0b0000000001)); + EXPECT_TRUE(dsl::detail::is_basic_mask(0b0000000000)); + + EXPECT_FALSE(dsl::detail::is_basic_mask(0x80000000)); + EXPECT_FALSE(dsl::detail::is_basic_mask(0b0000000010)); + EXPECT_FALSE(dsl::detail::is_basic_mask(0b0100011111)); + EXPECT_FALSE(dsl::detail::is_basic_mask(0b1111110111)); +} + +TEST(safe_dsl_test, fill_bitmask) { + EXPECT_EQ(dsl::detail::fill_in_bitmask(0b1111), 0b1111); + EXPECT_EQ(dsl::detail::fill_in_bitmask(0b1101), 0b1111); + EXPECT_EQ(dsl::detail::fill_in_bitmask(0b1000), 0b1111); + + EXPECT_EQ(dsl::detail::fill_in_bitmask(0x80000000), 0xffffffff); + EXPECT_EQ(dsl::detail::fill_in_bitmask(0x40000000), 0x7fffffff); +} + +TEST(safe_dsl_test, ival_pwr2_range_to_mask) { + using ival_mask_255 = dsl::detail::to_mask_t>; + EXPECT_EQ(ival_mask_255::var_bits, 255); + EXPECT_EQ(ival_mask_255::const_bits, 0); + + using ival_mask_15 = dsl::detail::to_mask_t>; + EXPECT_EQ(ival_mask_15::var_bits, 15); + EXPECT_EQ(ival_mask_15::const_bits, 0); +} + +TEST(safe_dsl_test, ival_0_12_to_mask) { + using ival_mask = dsl::detail::to_mask_t>; + EXPECT_EQ(ival_mask::var_bits, 0b1111); + EXPECT_EQ(ival_mask::const_bits, 0); +} + +TEST(safe_dsl_test, ival_0_137_to_mask) { + using ival_mask = dsl::detail::to_mask_t>; + EXPECT_EQ(ival_mask::var_bits, 0xff); + EXPECT_EQ(ival_mask::const_bits, 0); +} + +TEST(safe_dsl_test, ival_w_offset_to_mask_1) { + using ival_mask = dsl::detail::to_mask_t>; + EXPECT_EQ(ival_mask::var_bits, 0xff); + EXPECT_EQ(ival_mask::const_bits, 512); +} + +TEST(safe_dsl_test, ival_w_offset_to_mask_2) { + using ival_mask = dsl::detail::to_mask_t>; + EXPECT_EQ(ival_mask::var_bits, 0xff); + EXPECT_EQ(ival_mask::const_bits, 0); +} + +TEST(safe_dsl_test, ival_const_42_to_mask) { + using ival_mask = dsl::detail::to_mask_t>; + EXPECT_EQ(ival_mask::var_bits, 0); + EXPECT_EQ(ival_mask::const_bits, 42); +} + +TEST(safe_dsl_test, mask_14_to_ival) { + using mask_ival = dsl::detail::to_ival_t>; + EXPECT_EQ(mask_ival::min, 0); + EXPECT_EQ(mask_ival::max, 14); +} + +TEST(safe_dsl_test, mask_8_to_ival) { + using mask_ival = dsl::detail::to_ival_t>; + EXPECT_EQ(mask_ival::min, 0); + EXPECT_EQ(mask_ival::max, 8); +} + +TEST(safe_dsl_test, mask_8_256_to_ival) { + using mask_ival = dsl::detail::to_ival_t>; + EXPECT_EQ(mask_ival::min, 256); + EXPECT_EQ(mask_ival::max, 256 + 8); +} + +TEST(safe_dsl_test, mask_const_to_ival) { + using mask_ival = dsl::detail::to_ival_t>; + EXPECT_EQ(mask_ival::min, 42); + EXPECT_EQ(mask_ival::max, 42); +} + +TEST(safe_dsl_test, mask_ival_eq) { + EXPECT_TRUE((constrain_interval<0, 15> <= constrain_mask<0b1111>)); + EXPECT_TRUE((constrain_interval<0, 15> >= constrain_mask<0b1111>)); + + EXPECT_TRUE((constrain_interval<0, 15> == constrain_mask<0b1111>)); + + EXPECT_TRUE((constrain_interval<0, 14> >= constrain_mask<0b1110>)); + EXPECT_FALSE((constrain_interval<0, 14> <= constrain_mask<0b1110>)); + EXPECT_TRUE((constrain_interval<0, 14> != constrain_mask<0b1110>)); +} diff --git a/test/safe/dsl/divide.cpp b/test/safe/dsl/divide.cpp index 255eef3..4c584d1 100644 --- a/test/safe/dsl/divide.cpp +++ b/test/safe/dsl/divide.cpp @@ -11,9 +11,9 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_divide, divide_two_ival_constants) { - EXPECT_EQ((ival<48, 48> / ival<4, 4>), (ival<12, 12>)); + EXPECT_EQ((constrain_interval<48, 48> / constrain_interval<4, 4>), (constrain_interval<12, 12>)); } TEST(safe_dsl_divide, add_two_intervals) { - EXPECT_EQ((ival<10, 40> / ival<1, 5>), (ival<2, 40>)); + EXPECT_EQ((constrain_interval<10, 40> / constrain_interval<1, 5>), (constrain_interval<2, 40>)); } diff --git a/test/safe/dsl/intersection.cpp b/test/safe/dsl/intersection.cpp index ee0b4a1..1a7b43f 100644 --- a/test/safe/dsl/intersection.cpp +++ b/test/safe/dsl/intersection.cpp @@ -11,25 +11,25 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_intersection, eval_ival_overlap) { - EXPECT_EQ((ival<0, 20> && ival<10, 30>), (ival<10, 20>)); + EXPECT_EQ((constrain_interval<0, 20> && constrain_interval<10, 30>), (constrain_interval<10, 20>)); } TEST(safe_dsl_intersection, eval_lhs_ival_contained) { - EXPECT_EQ((ival<15, 25> && ival<10, 30>), (ival<15, 25>)); + EXPECT_EQ((constrain_interval<15, 25> && constrain_interval<10, 30>), (constrain_interval<15, 25>)); } TEST(safe_dsl_intersection, eval_rhs_ival_contained) { - EXPECT_EQ((ival<15, 25> && ival<19, 21>), (ival<19, 21>)); + EXPECT_EQ((constrain_interval<15, 25> && constrain_interval<19, 21>), (constrain_interval<19, 21>)); } TEST(safe_dsl_intersection, eval_single_int_1) { - EXPECT_EQ((ival<15, 25> && ival<25, 35>), (ival<25, 25>)); + EXPECT_EQ((constrain_interval<15, 25> && constrain_interval<25, 35>), (constrain_interval<25, 25>)); } TEST(safe_dsl_intersection, eval_single_int_2) { - EXPECT_EQ((ival<35, 50> && ival<25, 35>), (ival<35, 35>)); + EXPECT_EQ((constrain_interval<35, 50> && constrain_interval<25, 35>), (constrain_interval<35, 35>)); } TEST(safe_dsl_intersection, eval_three_ival_overlap) { - EXPECT_EQ((ival<0, 20> && ival<10, 30> && ival<-100, 15>), (ival<10, 15>)); + EXPECT_EQ((constrain_interval<0, 20> && constrain_interval<10, 30> && constrain_interval<-100, 15>), (constrain_interval<10, 15>)); } \ No newline at end of file diff --git a/test/safe/dsl/is_equal.cpp b/test/safe/dsl/is_equal.cpp index 31c0d5a..df1453a 100644 --- a/test/safe/dsl/is_equal.cpp +++ b/test/safe/dsl/is_equal.cpp @@ -11,19 +11,19 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_is_equal, simple_ivals) { - EXPECT_EQ((ival<10, 20>), (ival<10, 20>)); + EXPECT_EQ((constrain_interval<10, 20>), (constrain_interval<10, 20>)); - EXPECT_NE((ival<10, 20>), (ival<10, 10>)); + EXPECT_NE((constrain_interval<10, 20>), (constrain_interval<10, 10>)); } TEST(safe_dsl_is_equal, union_ivals) { - EXPECT_EQ((ival<10, 20> || ival<40, 50>), (ival<10, 20> || ival<40, 50>)); + EXPECT_EQ((constrain_interval<10, 20> || constrain_interval<40, 50>), (constrain_interval<10, 20> || constrain_interval<40, 50>)); - EXPECT_EQ((ival<10, 20> || ival<40, 50>), (ival<40, 50> || ival<10, 20>)); + EXPECT_EQ((constrain_interval<10, 20> || constrain_interval<40, 50>), (constrain_interval<40, 50> || constrain_interval<10, 20>)); } TEST(safe_dsl_is_equal, differing_ival_types) { - EXPECT_EQ((ival<10, 20>), (ival<10u, 20u>)); + EXPECT_EQ((constrain_interval<10, 20>), (constrain_interval<10u, 20u>)); - EXPECT_EQ((ival<10ll, 20ll>), (ival<10, 20>)); + EXPECT_EQ((constrain_interval<10ll, 20ll>), (constrain_interval<10, 20>)); } diff --git a/test/safe/dsl/is_subset.cpp b/test/safe/dsl/is_subset.cpp index 761d560..7b1c8ca 100644 --- a/test/safe/dsl/is_subset.cpp +++ b/test/safe/dsl/is_subset.cpp @@ -11,22 +11,22 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_is_subset, superset_v_subset_op) { - EXPECT_TRUE(!(ival<0, 100> >= ival<0, 101>)); - EXPECT_TRUE(!(ival<0, 100> >= ival<200, 300>)); - EXPECT_TRUE(!(ival<0, 100> <= ival<200, 300>)); - EXPECT_TRUE(!(ival<0, 200> >= ival<100, 300>)); - EXPECT_TRUE(!(ival<0, 200> <= ival<100, 300>)); + EXPECT_TRUE(!(constrain_interval<0, 100> >= constrain_interval<0, 101>)); + EXPECT_TRUE(!(constrain_interval<0, 100> >= constrain_interval<200, 300>)); + EXPECT_TRUE(!(constrain_interval<0, 100> <= constrain_interval<200, 300>)); + EXPECT_TRUE(!(constrain_interval<0, 200> >= constrain_interval<100, 300>)); + EXPECT_TRUE(!(constrain_interval<0, 200> <= constrain_interval<100, 300>)); } TEST(safe_dsl_is_subset, superset_op) { - EXPECT_TRUE((ival<0, 100> >= ival<10, 90>)); - EXPECT_TRUE((ival<0, 100> >= ival<0, 100>)); + EXPECT_TRUE((constrain_interval<0, 100> >= constrain_interval<10, 90>)); + EXPECT_TRUE((constrain_interval<0, 100> >= constrain_interval<0, 100>)); } TEST(safe_dsl_is_subset, superset_union_simplification) { - EXPECT_TRUE((ival<0, 100> >= (ival<10, 20> || ival<30, 40>))); - EXPECT_TRUE(((ival<0, 100> || ival<200, 300>) >= ival<10, 20>)); - EXPECT_TRUE(((ival<0, 100> || ival<200, 300>) >= ival<250, 300>)); + EXPECT_TRUE((constrain_interval<0, 100> >= (constrain_interval<10, 20> || constrain_interval<30, 40>))); + EXPECT_TRUE(((constrain_interval<0, 100> || constrain_interval<200, 300>) >= constrain_interval<10, 20>)); + EXPECT_TRUE(((constrain_interval<0, 100> || constrain_interval<200, 300>) >= constrain_interval<250, 300>)); EXPECT_TRUE( - ((ival<0, 100> || ival<200, 300>) >= (ival<10, 20> || ival<250, 300>))); + ((constrain_interval<0, 100> || constrain_interval<200, 300>) >= (constrain_interval<10, 20> || constrain_interval<250, 300>))); } \ No newline at end of file diff --git a/test/safe/dsl/mask.cpp b/test/safe/dsl/mask.cpp deleted file mode 100644 index 3099f07..0000000 --- a/test/safe/dsl/mask.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include - -using ::testing::_; -using ::testing::InSequence; -using ::testing::Return; - -using namespace safe; -using namespace safe::literals; - -TEST(safe_dsl_mask, create_mask) { [[maybe_unused]] auto v = mask<0b1001>; } - -TEST(safe_dsl_mask, mask_subset_var_only) { - EXPECT_TRUE(mask<0b1111> >= mask<0b1010>); - EXPECT_TRUE(mask<0b1111> >= mask<0b0000>); - EXPECT_TRUE(mask<0b1111> >= mask<0b1111>); - - EXPECT_FALSE(mask<0b1001> >= mask<0b1010>); - EXPECT_FALSE(mask<0b0001> >= mask<0b0010>); - EXPECT_FALSE(mask<0b0001> >= mask<0b0011>); -} - -TEST(safe_dsl_mask, mask_subset_const_only) { - EXPECT_TRUE((mask<0, 0b1111>) >= (mask<0, 0b1111>)); - - EXPECT_FALSE((mask<0, 0b1111>) >= (mask<0, 0b0000>)); - EXPECT_FALSE((mask<0, 0b1111>) >= (mask<0, 0b1010>)); - EXPECT_FALSE((mask<0, 0b1001>) >= (mask<0, 0b1010>)); - EXPECT_FALSE((mask<0, 0b0001>) >= (mask<0, 0b0010>)); - EXPECT_FALSE((mask<0, 0b0001>) >= (mask<0, 0b0011>)); -} - -TEST(safe_dsl_mask, mask_superset_var_only) { - EXPECT_TRUE(mask<0b1000> <= mask<0b1100>); - EXPECT_TRUE(mask<0b0000> <= mask<0b0000>); - EXPECT_TRUE(mask<0b0001> <= mask<0b1011>); - - EXPECT_FALSE(mask<0b1001> <= mask<0b1100>); - EXPECT_FALSE(mask<0b0010> <= mask<0b0000>); - EXPECT_FALSE(mask<0b0100> <= mask<0b1011>); -} - -TEST(safe_dsl_mask, mask_superset_const_only) { - EXPECT_TRUE((mask<0, 0b1111>) <= (mask<0, 0b1111>)); - - EXPECT_FALSE((mask<0, 0b1111>) <= (mask<0, 0b0000>)); - EXPECT_FALSE((mask<0, 0b1111>) <= (mask<0, 0b1010>)); - EXPECT_FALSE((mask<0, 0b1001>) <= (mask<0, 0b1010>)); - EXPECT_FALSE((mask<0, 0b0001>) <= (mask<0, 0b0010>)); - EXPECT_FALSE((mask<0, 0b0001>) <= (mask<0, 0b0011>)); -} - -TEST(safe_dsl_mask, mask_equal_var_only) { - EXPECT_TRUE(mask<0b1000> == mask<0b1000>); - EXPECT_TRUE(mask<0b0000> == mask<0b0000>); - EXPECT_TRUE(mask<0b0001> == mask<0b0001>); - - EXPECT_FALSE(mask<0b1000> == mask<0b1001>); - EXPECT_FALSE(mask<0b0011> == mask<0b0010>); - EXPECT_FALSE(mask<0b0110> == mask<0b0100>); -} - -TEST(safe_dsl_mask, mask_equal_const_only) { - EXPECT_TRUE((mask<0, 0b1000>) == (mask<0, 0b1000>)); - EXPECT_TRUE((mask<0, 0b0000>) == (mask<0, 0b0000>)); - EXPECT_TRUE((mask<0, 0b0001>) == (mask<0, 0b0001>)); - EXPECT_FALSE((mask<0, 0b1000>) == (mask<0, 0b1001>)); - EXPECT_FALSE((mask<0, 0b0011>) == (mask<0, 0b0010>)); - EXPECT_FALSE((mask<0, 0b0110>) == (mask<0, 0b0100>)); -} - -TEST(safe_dsl_mask, mask_not_equal_var_only) { - EXPECT_FALSE(mask<0b1000> != mask<0b1000>); - EXPECT_FALSE(mask<0b0000> != mask<0b0000>); - EXPECT_FALSE(mask<0b0001> != mask<0b0001>); - - EXPECT_TRUE(mask<0b1000> != mask<0b1001>); - EXPECT_TRUE(mask<0b0011> != mask<0b0010>); - EXPECT_TRUE(mask<0b0110> != mask<0b0100>); -} - -TEST(safe_dsl_mask, mask_not_equal_const_only) { - EXPECT_FALSE((mask<0, 0b1000>) != (mask<0, 0b1000>)); - EXPECT_FALSE((mask<0, 0b0000>) != (mask<0, 0b0000>)); - EXPECT_FALSE((mask<0, 0b0001>) != (mask<0, 0b0001>)); - EXPECT_TRUE((mask<0, 0b1000>) != (mask<0, 0b1001>)); - EXPECT_TRUE((mask<0, 0b0011>) != (mask<0, 0b0010>)); - EXPECT_TRUE((mask<0, 0b0110>) != (mask<0, 0b0100>)); -} - -TEST(safe_dsl_mask, detail_is_basic_mask) { - EXPECT_TRUE(dsl::detail::is_basic_mask(0xffffffff)); - EXPECT_TRUE(dsl::detail::is_basic_mask(0xffffffffu)); - - EXPECT_TRUE(dsl::detail::is_basic_mask(0b1111111111)); - EXPECT_TRUE(dsl::detail::is_basic_mask(0b0000111111)); - EXPECT_TRUE(dsl::detail::is_basic_mask(0b0000000001)); - EXPECT_TRUE(dsl::detail::is_basic_mask(0b0000000000)); - - EXPECT_FALSE(dsl::detail::is_basic_mask(0x80000000)); - EXPECT_FALSE(dsl::detail::is_basic_mask(0b0000000010)); - EXPECT_FALSE(dsl::detail::is_basic_mask(0b0100011111)); - EXPECT_FALSE(dsl::detail::is_basic_mask(0b1111110111)); -} - -TEST(safe_dsl_test, fill_bitmask) { - EXPECT_EQ(dsl::detail::fill_in_bitmask(0b1111), 0b1111); - EXPECT_EQ(dsl::detail::fill_in_bitmask(0b1101), 0b1111); - EXPECT_EQ(dsl::detail::fill_in_bitmask(0b1000), 0b1111); - - EXPECT_EQ(dsl::detail::fill_in_bitmask(0x80000000), 0xffffffff); - EXPECT_EQ(dsl::detail::fill_in_bitmask(0x40000000), 0x7fffffff); -} - -TEST(safe_dsl_test, ival_pwr2_range_to_mask) { - using ival_mask_255 = dsl::detail::to_mask_t>; - EXPECT_EQ(ival_mask_255::var_bits, 255); - EXPECT_EQ(ival_mask_255::const_bits, 0); - - using ival_mask_15 = dsl::detail::to_mask_t>; - EXPECT_EQ(ival_mask_15::var_bits, 15); - EXPECT_EQ(ival_mask_15::const_bits, 0); -} - -TEST(safe_dsl_test, ival_0_12_to_mask) { - using ival_mask = dsl::detail::to_mask_t>; - EXPECT_EQ(ival_mask::var_bits, 0b1111); - EXPECT_EQ(ival_mask::const_bits, 0); -} - -TEST(safe_dsl_test, ival_0_137_to_mask) { - using ival_mask = dsl::detail::to_mask_t>; - EXPECT_EQ(ival_mask::var_bits, 0xff); - EXPECT_EQ(ival_mask::const_bits, 0); -} - -TEST(safe_dsl_test, ival_w_offset_to_mask_1) { - using ival_mask = dsl::detail::to_mask_t>; - EXPECT_EQ(ival_mask::var_bits, 0xff); - EXPECT_EQ(ival_mask::const_bits, 512); -} - -TEST(safe_dsl_test, ival_w_offset_to_mask_2) { - using ival_mask = dsl::detail::to_mask_t>; - EXPECT_EQ(ival_mask::var_bits, 0xff); - EXPECT_EQ(ival_mask::const_bits, 0); -} - -TEST(safe_dsl_test, ival_const_42_to_mask) { - using ival_mask = dsl::detail::to_mask_t>; - EXPECT_EQ(ival_mask::var_bits, 0); - EXPECT_EQ(ival_mask::const_bits, 42); -} - -TEST(safe_dsl_test, mask_14_to_ival) { - using mask_ival = dsl::detail::to_ival_t>; - EXPECT_EQ(mask_ival::min, 0); - EXPECT_EQ(mask_ival::max, 14); -} - -TEST(safe_dsl_test, mask_8_to_ival) { - using mask_ival = dsl::detail::to_ival_t>; - EXPECT_EQ(mask_ival::min, 0); - EXPECT_EQ(mask_ival::max, 8); -} - -TEST(safe_dsl_test, mask_8_256_to_ival) { - using mask_ival = dsl::detail::to_ival_t>; - EXPECT_EQ(mask_ival::min, 256); - EXPECT_EQ(mask_ival::max, 256 + 8); -} - -TEST(safe_dsl_test, mask_const_to_ival) { - using mask_ival = dsl::detail::to_ival_t>; - EXPECT_EQ(mask_ival::min, 42); - EXPECT_EQ(mask_ival::max, 42); -} - -TEST(safe_dsl_test, mask_ival_eq) { - EXPECT_TRUE((ival<0, 15> <= mask<0b1111>)); - EXPECT_TRUE((ival<0, 15> >= mask<0b1111>)); - - EXPECT_TRUE((ival<0, 15> == mask<0b1111>)); - - EXPECT_TRUE((ival<0, 14> >= mask<0b1110>)); - EXPECT_FALSE((ival<0, 14> <= mask<0b1110>)); - EXPECT_TRUE((ival<0, 14> != mask<0b1110>)); -} diff --git a/test/safe/dsl/minus.cpp b/test/safe/dsl/minus.cpp index ed96892..6341a65 100644 --- a/test/safe/dsl/minus.cpp +++ b/test/safe/dsl/minus.cpp @@ -11,19 +11,19 @@ using namespace safe; using namespace safe::literals; TEST(safe_dsl_minus, sub_two_ival_constants) { - EXPECT_EQ((ival<30, 30> - ival<12, 12>), (ival<18, 18>)); + EXPECT_EQ((constrain_interval<30, 30> - constrain_interval<12, 12>), (constrain_interval<18, 18>)); } TEST(safe_dsl_minus, sub_two_intervals) { - EXPECT_EQ((ival<10, 20> - ival<40, 80>), (ival<-70, -20>)); + EXPECT_EQ((constrain_interval<10, 20> - constrain_interval<40, 80>), (constrain_interval<-70, -20>)); } TEST(safe_dsl_minus, sub_two_mask_constants) { - EXPECT_EQ((mask<0, 12> - mask<0, 8>), (mask<0, 4>)); + EXPECT_EQ((constrain_mask<0, 12> - constrain_mask<0, 8>), (constrain_mask<0, 4>)); } // TEST(safe_dsl_minus, sub_two_masks_1) { -// auto const actual = dsl::detail::simp(mask<0x3> - mask<0x3>); +// auto const actual = dsl::detail::simp(constrain_mask<0x3> - constrain_mask<0x3>); // EXPECT_EQ( // actual.var_bits, @@ -33,21 +33,21 @@ TEST(safe_dsl_minus, sub_two_mask_constants) { // TEST(safe_dsl_minus, sub_two_masks_2) { // EXPECT_EQ( -// (mask<0xf> - mask<0xff>), -// (mask<0xffff'ffff>) +// (constrain_mask<0xf> - constrain_mask<0xff>), +// (constrain_mask<0xffff'ffff>) // ); // } // TEST(safe_dsl_minus, sub_two_masks_3) { // EXPECT_EQ( -// (mask<0, 13> - mask<0, 29>), -// (mask<0, -16>) +// (constrain_mask<0, 13> - constrain_mask<0, 29>), +// (constrain_mask<0, -16>) // ); // } // TEST(safe_dsl_minus, sub_two_masks_4) { // EXPECT_EQ( -// (mask<0x11> - mask<0x11>), -// (mask<0xffff'ffff>) +// (constrain_mask<0x11> - constrain_mask<0x11>), +// (constrain_mask<0xffff'ffff>) // ); // } diff --git a/test/safe/match.cpp b/test/safe/match.cpp index b0c9a76..f9d0564 100644 --- a/test/safe/match.cpp +++ b/test/safe/match.cpp @@ -11,7 +11,7 @@ using namespace safe::interval_types; using namespace safe::int_types; using namespace safe::literals; -using safe::ival; +using safe::constrain_interval; struct mock_function { MOCK_METHOD(void, two_safe_vars, (int, int)); @@ -104,7 +104,7 @@ TEST(safe_runtime_check_test, single_interval_var) { } TEST(safe_runtime_check_test, interval_union) { - using safe_var = safe::var || ival<20, 30>>; + using safe_var = safe::constrained_number || constrain_interval<20, 30>, int>; EXPECT_TRUE((safe::detail::check(0))); EXPECT_TRUE((safe::detail::check(5))); diff --git a/test/safe/var/assign_lhs_rhs_union.cpp b/test/safe/var/assign_lhs_rhs_union.cpp deleted file mode 100644 index c354da3..0000000 --- a/test/safe/var/assign_lhs_rhs_union.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include - -using namespace safe::literals; -using safe::ival; - -void test() { - safe::var || ival<20, 30>> a = 0_s32; - safe::var || ival<21, 29>> b = 11_s32; - a = b; -} diff --git a/test/safe/var/assign_lhs_union.cpp b/test/safe/var/assign_lhs_union.cpp deleted file mode 100644 index 7b44371..0000000 --- a/test/safe/var/assign_lhs_union.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include - -using namespace safe::literals; -using safe::ival; - -void test() { - safe::var || ival<20, 30>> a = 0_s32; - safe::var> b = 11_s32; - a = b; -} diff --git a/test/safe/var/assign_rhs_union.cpp b/test/safe/var/assign_rhs_union.cpp deleted file mode 100644 index 5aaee2b..0000000 --- a/test/safe/var/assign_rhs_union.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include - -using namespace safe::literals; -using safe::ival; - -void test() { - safe::var> a = 1_s32; - safe::var || ival<21, 29>> b = 0_s32; - a = b; -} From d2af6ef5cd8e62d1670e5fab39336e06ec66dcf7 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Thu, 21 Mar 2024 12:13:00 +0900 Subject: [PATCH 02/13] remove big_integer tests in preperation to remove big_integer --- docs/big_integer.adoc | 54 ------- docs/index.adoc | 1 - docs/motivation.adoc | 1 - include/safe/dsl/constraint_invalid.hpp | 5 + test/safe/CMakeLists.txt | 13 -- test/safe/big_integer/detail/bit_and.cpp | 55 ------- test/safe/big_integer/detail/bit_not.cpp | 24 --- test/safe/big_integer/detail/bit_or.cpp | 55 ------- test/safe/big_integer/detail/bit_xor.cpp | 55 ------- test/safe/big_integer/detail/compare.cpp | 55 ------- test/safe/big_integer/detail/divides.cpp | 107 ------------- test/safe/big_integer/detail/minus.cpp | 29 ---- test/safe/big_integer/detail/multiplies.cpp | 72 --------- test/safe/big_integer/detail/negate.cpp | 25 ---- test/safe/big_integer/detail/plus.cpp | 49 ------ test/safe/big_integer/detail/shift.cpp | 106 ------------- test/safe/big_integer/detail/storage.cpp | 157 -------------------- 17 files changed, 5 insertions(+), 858 deletions(-) delete mode 100644 docs/big_integer.adoc create mode 100644 include/safe/dsl/constraint_invalid.hpp delete mode 100644 test/safe/big_integer/detail/bit_and.cpp delete mode 100644 test/safe/big_integer/detail/bit_not.cpp delete mode 100644 test/safe/big_integer/detail/bit_or.cpp delete mode 100644 test/safe/big_integer/detail/bit_xor.cpp delete mode 100644 test/safe/big_integer/detail/compare.cpp delete mode 100644 test/safe/big_integer/detail/divides.cpp delete mode 100644 test/safe/big_integer/detail/minus.cpp delete mode 100644 test/safe/big_integer/detail/multiplies.cpp delete mode 100644 test/safe/big_integer/detail/negate.cpp delete mode 100644 test/safe/big_integer/detail/plus.cpp delete mode 100644 test/safe/big_integer/detail/shift.cpp delete mode 100644 test/safe/big_integer/detail/storage.cpp diff --git a/docs/big_integer.adoc b/docs/big_integer.adoc deleted file mode 100644 index b8b7f6f..0000000 --- a/docs/big_integer.adoc +++ /dev/null @@ -1,54 +0,0 @@ -== Arbitrary Precision Integers - -=== Introduction to safe::big_integer - -There are a couple uses for an arbitrary precision `big_integer`: - -1. They are convenient to use when calculating bounds at compile-time. -2. They can be helpful to use at runtime for values exceeding standard C++20 - integer widths. - -While they are useful, both the API and design must be carefully considered in -order to minimize compile-time and runtime performance impacts and arithmetic -correctness with respect to overflow and underflow conditions. - -In order to meet these basic requirements, the following design rules -are selected: - -1. No dynamic memory allocation. Integer types will be represented by a type - templated by its bit width. -2. Specialized integer promotion rules. The return type of an operation - _should_ be wide enough to represent any result without overflow or - underflow. -3. No C++ exceptions. Exceptional conditions will not be handled by - `big_integer`. For user code, `big_integer` should always be wrapped in a - `safe::constrained_number` to provide protection from unsafe operations. -4. `big_integer` shall be signed and there will be no unsigned version. -5. Seamless interop with standard integral types. - - -=== Examples - - - -=== Design - -The `big_integer` type is implemented in two major components: - -1. `detail::storage` contains the low-level implementation of the integer - representation and operations. Results are returned through reference - parameters. This is done to decouple result bit-width from the operation - implementation. -2. `interface::big_integer` contains the outward-facing interface of - `big_integer`. It has implementations for the integral operators and behaves - much like a regular integral type. - -Concepts are used to ensure the operator functions have at least one -`big_integer` parameter while the other parameter could be a built-in integral -type, `std::integral_constant`, or another `big_integer`. - -Almost all operations return a `big_integer` wide enough for the result without -overflow or underflow. The exception is `operator<<`, which may overflow when -given a runtime value. If `operator<<` is given a `std::integral_constant` -shift amount, then the resulting type will be widened by the size of the shift -amount. In that case, no overflow or underflow is possible. \ No newline at end of file diff --git a/docs/index.adoc b/docs/index.adoc index 1d57894..180c8ff 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -14,6 +14,5 @@ Luke Valenty include::motivation.adoc[] include::overview.adoc[] -include::big_integer.adoc[] include::api_reference.adoc[] include::theory.adoc[] \ No newline at end of file diff --git a/docs/motivation.adoc b/docs/motivation.adoc index 7aaee47..e3e0ba4 100644 --- a/docs/motivation.adoc +++ b/docs/motivation.adoc @@ -54,7 +54,6 @@ The current version of the Safe Arithmetic library supports the following value types: * C++20 integer primitives -* `safe::big_integer` arbitrary precision integers In the future, it is likely support for the following numeric types will be added: diff --git a/include/safe/dsl/constraint_invalid.hpp b/include/safe/dsl/constraint_invalid.hpp new file mode 100644 index 0000000..bdcfbcb --- /dev/null +++ b/include/safe/dsl/constraint_invalid.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace safe::dsl { + struct constraint_invalid_t {}; +} \ No newline at end of file diff --git a/test/safe/CMakeLists.txt b/test/safe/CMakeLists.txt index 629aaa6..ca3bfb6 100644 --- a/test/safe/CMakeLists.txt +++ b/test/safe/CMakeLists.txt @@ -18,19 +18,6 @@ function(add_test_suites) endfunction() add_test_suites( - big_integer/detail/storage.cpp - big_integer/detail/plus.cpp - big_integer/detail/minus.cpp - big_integer/detail/negate.cpp - big_integer/detail/bit_and.cpp - big_integer/detail/bit_or.cpp - big_integer/detail/bit_xor.cpp - big_integer/detail/bit_not.cpp - big_integer/detail/shift.cpp - big_integer/detail/multiplies.cpp - big_integer/detail/compare.cpp - big_integer/detail/divides.cpp - big_integer.cpp constrained_number.cpp match.cpp array.cpp diff --git a/test/safe/big_integer/detail/bit_and.cpp b/test/safe/big_integer/detail/bit_and.cpp deleted file mode 100644 index 63c8c9e..0000000 --- a/test/safe/big_integer/detail/bit_and.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(bit_and, is_commutative, (storage<128> a, storage<128> b)) { - is_commutative(bit_and, a, b); -} - -RC_GTEST_PROP(bit_and, asymmetric_is_commutative, - (storage<128> a, storage<64> b)) { - is_commutative(bit_and, a, b); -} - -RC_GTEST_PROP(bit_and, is_idempotent, (storage<128> a)) { - is_idempotent(bit_and, a); -} - -RC_GTEST_PROP(bit_and, identity, (storage<128> a)) { - identity<128, storage<32>{{0xffff'ffffu}}>(bit_and, a); -} - -RC_GTEST_PROP(bit_and, domination, (storage<128> a)) { - domination<128, storage<32>{}>(bit_and, a); -} - -RC_GTEST_PROP(bit_and, is_associative, - (storage<128> a, storage<128> b, storage<128> c)) { - is_associative>(bit_and, a, b, c); -} - -RC_GTEST_PROP(bit_and, is_and, (int64_t a, int64_t b)) { - storage<64> actual{}; - bit_and(actual, make_storage(a), make_storage(b)); - storage<64> expected = make_storage(a & b); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(bit_and, lhs_can_be_result, (storage<256> a, storage<256> b)) { - lhs_can_be_result>(bit_and, a, b); -} - -TEST(bit_and, basic_operation) { - storage<128> lhs{{0x0000'ffff, 0x1030'ffff, 0x0550'ffff, 0x0000'5555}}; - storage<128> rhs{{0xffff'0000, 0xffff'0608, 0xffff'0000, 0xffff'0000}}; - storage<128> expected{{0x0000'0000, 0x1030'0608, 0x0550'0000, 0x0000'0000}}; - bit_and(lhs, lhs, rhs); - ASSERT_EQ(expected, lhs); -} -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/test/safe/big_integer/detail/bit_not.cpp b/test/safe/big_integer/detail/bit_not.cpp deleted file mode 100644 index 3ca35ca..0000000 --- a/test/safe/big_integer/detail/bit_not.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(bit_not, not_not_is_self, (storage<128> a)) { - storage<128> r{}; - bit_not(r, a); - bit_not(r, r); - RC_ASSERT(r == a); -} - -RC_GTEST_PROP(bit_not, is_not, (int64_t a)) { - storage<64> actual{}; - bit_not(actual, make_storage(a)); - storage<64> expected = make_storage(~a); - RC_ASSERT(actual == expected); -} -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/test/safe/big_integer/detail/bit_or.cpp b/test/safe/big_integer/detail/bit_or.cpp deleted file mode 100644 index 4b068d3..0000000 --- a/test/safe/big_integer/detail/bit_or.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(bit_or, is_commutative, (storage<128> a, storage<128> b)) { - is_commutative(bit_or, a, b); -} - -RC_GTEST_PROP(bit_or, asymmetric_is_commutative, - (storage<128> a, storage<64> b)) { - is_commutative(bit_or, a, b); -} - -RC_GTEST_PROP(bit_or, is_idempotent, (storage<128> a)) { - is_idempotent(bit_or, a); -} - -RC_GTEST_PROP(bit_or, identity, (storage<128> a)) { - identity<128, storage<32>{}>(bit_or, a); -} - -RC_GTEST_PROP(bit_or, domination, (storage<128> a)) { - domination<128, storage<32>{{0xffff'ffffu}}>(bit_or, a); -} - -RC_GTEST_PROP(bit_or, is_associative, - (storage<128> a, storage<128> b, storage<128> c)) { - is_associative>(bit_or, a, b, c); -} - -RC_GTEST_PROP(bit_or, is_or, (int64_t a, int64_t b)) { - storage<64> actual{}; - bit_or(actual, make_storage(a), make_storage(b)); - storage<64> expected = make_storage(a | b); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(bit_or, lhs_can_be_result, (storage<256> a, storage<256> b)) { - lhs_can_be_result>(bit_or, a, b); -} - -TEST(bit_or, basic_opperation) { - storage<128> lhs{{0x0000'cafe, 0x1030'5070, 0x0550'0000, 0x0000'5555}}; - storage<128> rhs{{0xffff'0000, 0x0204'0608, 0x7007'0000, 0x1111'0000}}; - storage<128> expected{{0xffff'cafe, 0x1234'5678, 0x7557'0000, 0x1111'5555}}; - bit_or(lhs, lhs, rhs); - ASSERT_EQ(expected, lhs); -} -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/test/safe/big_integer/detail/bit_xor.cpp b/test/safe/big_integer/detail/bit_xor.cpp deleted file mode 100644 index e43a831..0000000 --- a/test/safe/big_integer/detail/bit_xor.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(bit_xor, is_commutative, (storage<128> a, storage<128> b)) { - is_commutative(bit_xor, a, b); -} - -RC_GTEST_PROP(bit_xor, asymmetric_is_commutative, - (storage<128> a, storage<64> b)) { - is_commutative(bit_xor, a, b); -} - -RC_GTEST_PROP(bit_xor, is_associative, - (storage<128> a, storage<128> b, storage<128> c)) { - is_associative>(bit_xor, a, b, c); -} - -RC_GTEST_PROP(bit_xor, identity, (storage<128> a)) { - identity<128, storage<32>{}>(bit_xor, a); -} - -RC_GTEST_PROP(bit_xor, self_is_domination, (storage<128> a)) { - storage<128> r{}; - bit_xor(r, a, a); - storage<128> zero{}; - - RC_ASSERT(r == zero); -} - -RC_GTEST_PROP(bit_xor, is_xor, (int64_t a, int64_t b)) { - storage<64> actual{}; - bit_xor(actual, make_storage(a), make_storage(b)); - storage<64> expected = make_storage(a ^ b); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(bit_xor, lhs_can_be_result, (storage<256> a, storage<256> b)) { - lhs_can_be_result>(bit_xor, a, b); -} - -TEST(bit_xor, basic_operation) { - storage<128> lhs{{0x0000'ffff, 0x1030'ffff, 0x0550'ffff, 0x0000'5555}}; - storage<128> rhs{{0xffff'0000, 0xffff'0608, 0xffff'0000, 0xffff'0000}}; - storage<128> expected{{0xffff'ffff, 0xefcf'f9f7, 0xfaaf'ffff, 0xffff'5555}}; - bit_xor(lhs, lhs, rhs); - ASSERT_EQ(expected, lhs); -} -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/test/safe/big_integer/detail/compare.cpp b/test/safe/big_integer/detail/compare.cpp deleted file mode 100644 index 88f6cad..0000000 --- a/test/safe/big_integer/detail/compare.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(compare, equality_256, (storage<256> a)) { - RC_ASSERT((a <=> a) == std::strong_ordering::equal); -} - -RC_GTEST_PROP(compare, equality_32, (storage<32> a)) { - RC_ASSERT((a <=> a) == std::strong_ordering::equal); -} - -RC_GTEST_PROP(compare, asymmetric_equality_256, (storage<256> a)) { - storage<512> b{a}; - RC_ASSERT((a <=> b) == std::strong_ordering::equal); -} - -RC_GTEST_PROP(compare, inequality_by_plus, (storage<256> a)) { - storage<257> b{}; - plus(b, a, make_storage(1)); - RC_ASSERT((a <=> b) == std::strong_ordering::less); - RC_ASSERT((b <=> a) == std::strong_ordering::greater); -} - -RC_GTEST_PROP(compare, is_compare_64, (int64_t a, int64_t b)) { - std::strong_ordering const actual = make_storage(a) <=> make_storage(b); - std::strong_ordering const expected = (a <=> b); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(compare, is_compare_32, (int32_t a, int32_t b)) { - std::strong_ordering const actual = make_storage(a) <=> make_storage(b); - std::strong_ordering const expected = (a <=> b); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(compare, is_compare_32_64, (int32_t a, int64_t b)) { - std::strong_ordering const actual = make_storage(a) <=> make_storage(b); - std::strong_ordering const expected = (a <=> b); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(compare, is_compare_64_32, (int64_t a, int32_t b)) { - std::strong_ordering const actual = make_storage(a) <=> make_storage(b); - std::strong_ordering const expected = (a <=> b); - RC_ASSERT(actual == expected); -} -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/test/safe/big_integer/detail/divides.cpp b/test/safe/big_integer/detail/divides.cpp deleted file mode 100644 index f1be4f2..0000000 --- a/test/safe/big_integer/detail/divides.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(unsigned_divmod, identity, (storage<128> dividend)) { - RC_PRE(!dividend.negative()); - storage<128> q{}; - storage<128> r{}; - unsigned_divmod(q, r, dividend, make_storage(1)); - RC_ASSERT(q == dividend); - RC_ASSERT(r == make_storage(0)); -} - -RC_GTEST_PROP(unsigned_divmod, dividend_is_zero, (storage<128> divisor)) { - RC_PRE(!divisor.negative()); - RC_PRE(divisor != make_storage(0)); - storage<128> q{}; - storage<128> r{}; - unsigned_divmod(q, r, make_storage(0), divisor); - RC_ASSERT(q == make_storage(0)); - RC_ASSERT(r == make_storage(0)); -} - -RC_GTEST_PROP(unsigned_divmod, divide_then_multiply, - (storage<128> dividend, storage<128> divisor)) { - RC_PRE(!divisor.negative()); - RC_PRE(!dividend.negative()); - RC_PRE(divisor != make_storage(0)); - - storage<128> q{}; - storage<128> r{}; - - unsigned_divmod(q, r, dividend, divisor); - - storage<128> dividend_prime{}; - multiplies(dividend_prime, q, divisor); - plus(dividend_prime, dividend_prime, r); - - RC_ASSERT(dividend_prime == dividend); -} - -RC_GTEST_PROP(divmod, identity, (storage<128> dividend)) { - storage<128> q{}; - storage<128> r{}; - divmod(q, r, dividend, make_storage(1)); - RC_ASSERT(q == dividend); - RC_ASSERT(r == make_storage(0)); -} - -RC_GTEST_PROP(divmod, dividend_is_zero, (storage<128> divisor)) { - RC_PRE(divisor != make_storage(0)); - storage<128> q{}; - storage<128> r{}; - divmod(q, r, make_storage(0), divisor); - RC_ASSERT(q == make_storage(0)); - RC_ASSERT(r == make_storage(0)); -} - -RC_GTEST_PROP(divmod, divide_then_multiply, - (storage<256> dividend, storage<256> divisor)) { - RC_PRE(divisor != make_storage(0)); - - storage<256> q{}; - storage<256> r{}; - - divmod(q, r, dividend, divisor); - - storage<256> dividend_prime{}; - multiplies(dividend_prime, q, divisor); - plus(dividend_prime, dividend_prime, r); - - RC_ASSERT(dividend_prime == dividend); -} - -RC_GTEST_PROP(divmod, is_divide_and_remainder_64, - (int64_t dividend, int64_t divisor)) { - RC_PRE(divisor != 0); - - storage<64> q{}; - storage<64> r{}; - - divmod(q, r, make_storage(dividend), make_storage(divisor)); - - RC_ASSERT(make_storage(dividend / divisor) == q); - RC_ASSERT(make_storage(dividend % divisor) == r); -} - -RC_GTEST_PROP(divmod, is_divide_and_remainder_32, - (int32_t dividend, int32_t divisor)) { - RC_PRE(divisor != 0); - - storage<32> q{}; - storage<32> r{}; - - divmod(q, r, make_storage(dividend), make_storage(divisor)); - - RC_ASSERT(make_storage(dividend / divisor) == q); - RC_ASSERT(make_storage(dividend % divisor) == r); -} -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/test/safe/big_integer/detail/minus.cpp b/test/safe/big_integer/detail/minus.cpp deleted file mode 100644 index 92793eb..0000000 --- a/test/safe/big_integer/detail/minus.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(minus, identity, (storage<128> a)) { - identity<128, storage<32>{}>(minus, a); -} - -RC_GTEST_PROP(minus, self_is_domination, (storage<128> a)) { - storage<128> actual{}; - minus(actual, a, a); - storage<128> zero{}; - RC_ASSERT(actual == zero); -} - -RC_GTEST_PROP(minus, is_minus, (int64_t a, int64_t b)) { - RC_PRE(subtraction_will_not_overflow(a, b)); - storage<64> actual{}; - minus(actual, make_storage(a), make_storage(b)); - storage<64> expected = make_storage(a - b); - RC_ASSERT(actual == expected); -} -} // namespace safe::_big_integer::detail diff --git a/test/safe/big_integer/detail/multiplies.cpp b/test/safe/big_integer/detail/multiplies.cpp deleted file mode 100644 index e4a2056..0000000 --- a/test/safe/big_integer/detail/multiplies.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(multiplies, is_commutative, (storage<256> a, storage<256> b)) { - is_commutative(multiplies, a, b); -} - -RC_GTEST_PROP(multiplies, asymmetric_is_commutative, - (storage<256> a, storage<128> b)) { - is_commutative(multiplies, a, b); -} - -RC_GTEST_PROP(multiplies, identity, (storage<128> a)) { - identity<128, make_storage(1)>(multiplies, a); -} - -RC_GTEST_PROP(multiplies, is_associative_32, - (storage<32> a, storage<32> b, storage<32> c)) { - is_associative>(multiplies, a, b, c); -} - -RC_GTEST_PROP(multiplies, is_associative_64, - (storage<64> a, storage<64> b, storage<64> c)) { - is_associative>(multiplies, a, b, c); -} - -RC_GTEST_PROP(multiplies, is_associative_128, - (storage<128> a, storage<128> b, storage<128> c)) { - is_associative>(multiplies, a, b, c); -} - -RC_GTEST_PROP(multiplies, domination, (storage<128> a)) { - domination<128, make_storage(0)>(multiplies, a); -} - -RC_GTEST_PROP(multiplies, is_multiply_64, (int32_t a, int32_t b)) { - auto a64 = static_cast(a); - auto b64 = static_cast(b); - storage<64> actual{}; - multiplies(actual, make_storage(a64), make_storage(b64)); - storage<64> expected = make_storage(a64 * b64); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(multiplies, is_multiply_32, (int32_t a, int32_t b)) { - storage<64> actual{}; - multiplies(actual, make_storage(a), make_storage(b)); - storage<64> expected = - make_storage(static_cast(a) * static_cast(b)); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(multiplies, shift_and_multiply_equivalence, (storage<512> a)) { - auto const i = *rc::gen::inRange(0, 511); - - storage<1024> shift_result{}; - bit_shift_left(shift_result, a, i); - - storage<1024> shifted_one{}; - bit_shift_left(shifted_one, make_storage(1), i); - storage<1024> mul_result{}; - multiplies(mul_result, shifted_one, a); - RC_ASSERT(shift_result == mul_result); -} -} // namespace safe::_big_integer::detail diff --git a/test/safe/big_integer/detail/negate.cpp b/test/safe/big_integer/detail/negate.cpp deleted file mode 100644 index f7b5ff8..0000000 --- a/test/safe/big_integer/detail/negate.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(negate, negate_negate_is_self, (storage<128> a)) { - storage<128> r{}; - negate(r, a); - negate(r, r); - RC_ASSERT(r == a); -} - -RC_GTEST_PROP(negate, is_negate, (int64_t a)) { - RC_PRE(negation_will_not_overflow(a)); - storage<64> actual{}; - negate(actual, make_storage(a)); - storage<64> expected = make_storage(-a); - RC_ASSERT(actual == expected); -} -} // namespace safe::_big_integer::detail diff --git a/test/safe/big_integer/detail/plus.cpp b/test/safe/big_integer/detail/plus.cpp deleted file mode 100644 index f7b7b67..0000000 --- a/test/safe/big_integer/detail/plus.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(plus, is_commutative, (storage<128> a, storage<128> b)) { - is_commutative(plus, a, b); -} - -RC_GTEST_PROP(plus, asymmetric_is_commutative, - (storage<256> a, storage<128> b)) { - is_commutative(plus, a, b); -} - -RC_GTEST_PROP(plus, identity, (storage<128> a)) { - identity<128, storage<32>{}>(plus, a); -} - -RC_GTEST_PROP(plus, is_associative, - (storage<128> a, storage<128> b, storage<128> c)) { - is_associative>(plus, a, b, c); -} - -RC_GTEST_PROP(plus, negative_self_is_domination, (storage<128> a)) { - storage<128> neg_a{}; - negate(neg_a, a); - storage<128> actual{}; - plus(actual, a, neg_a); - storage<128> zero{}; - RC_ASSERT(actual == zero); -} - -RC_GTEST_PROP(plus, is_plus, (int64_t a, int64_t b)) { - RC_PRE(addition_will_not_overflow(a, b)); - storage<64> actual{}; - plus(actual, make_storage(a), make_storage(b)); - storage<64> expected = make_storage(a + b); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(plus, lhs_can_be_result, (storage<256> a, storage<256> b)) { - lhs_can_be_result>(plus, a, b); -} -} // namespace safe::_big_integer::detail diff --git a/test/safe/big_integer/detail/shift.cpp b/test/safe/big_integer/detail/shift.cpp deleted file mode 100644 index 75356e6..0000000 --- a/test/safe/big_integer/detail/shift.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(shift, identity_shift_left, (storage<128> a)) { - identity<128, 0>(bit_shift_left, a); -} - -RC_GTEST_PROP(shift, identity_shift_right, (storage<128> a)) { - identity<128, 0>(bit_shift_right, a); -} - -RC_GTEST_PROP(shift, shift_left_by_one_same_as_plus_itself, (storage<512> a)) { - storage<512> shift_result{}; - bit_shift_left(shift_result, a, 1); - storage<512> double_result{}; - plus(double_result, a, a); - RC_ASSERT(shift_result == double_result); -} - -RC_GTEST_PROP(shift, is_shift_left_64, (int64_t a)) { - auto const i = *rc::gen::inRange(0, 63); - storage<64> actual{}; - bit_shift_left(actual, make_storage(a), i); - storage<64> expected = make_storage(a << i); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(shift, is_shift_left_32, (int32_t a)) { - auto const i = *rc::gen::inRange(0, 31); - storage<32> actual{}; - bit_shift_left(actual, make_storage(a), i); - storage<32> expected = make_storage(a << i); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(shift, shift_left_order_doesnt_matter, (storage<512> a)) { - auto const i = *rc::gen::inRange(0, 255); - auto const j = *rc::gen::inRange(0, 255); - storage<512> x0{}; - bit_shift_left(x0, a, i); - storage<512> x1{}; - bit_shift_left(x1, x0, j); - storage<512> y0{}; - bit_shift_left(y0, a, j); - storage<512> y1{}; - bit_shift_left(y1, y0, i); - RC_ASSERT(x1 == y1); -} - -RC_GTEST_PROP(shift, shift_right_order_doesnt_matter, (storage<512> a)) { - auto const i = *rc::gen::inRange(0, 255); - auto const j = *rc::gen::inRange(0, 255); - storage<512> x0{}; - bit_shift_right(x0, a, i); - storage<512> x1{}; - bit_shift_right(x1, x0, j); - storage<512> y0{}; - bit_shift_right(y0, a, j); - storage<512> y1{}; - bit_shift_right(y1, y0, i); - RC_ASSERT(x1 == y1); -} - -RC_GTEST_PROP(shift, lhs_can_be_result_shift_left, (storage<256> a)) { - auto const i = *rc::gen::inRange(0, 255); - lhs_can_be_result>(bit_shift_left, a, i); -} - -RC_GTEST_PROP(shift, lhs_can_be_result_shift_right, (storage<256> a)) { - auto const i = *rc::gen::inRange(0, 255); - lhs_can_be_result>(bit_shift_right, a, i); -} - -RC_GTEST_PROP(shift, shift_left_then_right_is_same, (storage<256> a)) { - auto const i = *rc::gen::inRange(0, 255); - storage<512> r0{}; - bit_shift_left(r0, a, i); - storage<512> r1{}; - bit_shift_right(r1, r0, i); - RC_ASSERT(a == r1); -} - -RC_GTEST_PROP(shift, is_shift_right_64, (int64_t a)) { - auto const i = *rc::gen::inRange(0, 63); - storage<64> actual{}; - bit_shift_right(actual, make_storage(a), i); - storage<64> expected = make_storage(a >> i); - RC_ASSERT(actual == expected); -} - -RC_GTEST_PROP(shift, is_shift_right_32, (int32_t a)) { - auto const i = *rc::gen::inRange(0, 31); - storage<32> actual{}; - bit_shift_right(actual, make_storage(a), i); - storage<32> expected = make_storage(a >> i); - RC_ASSERT(actual == expected); -} -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/test/safe/big_integer/detail/storage.cpp b/test/safe/big_integer/detail/storage.cpp deleted file mode 100644 index 7d38523..0000000 --- a/test/safe/big_integer/detail/storage.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "properties.hpp" -#include "storage_gen.hpp" - -#include - -#include -#include - -namespace safe::_big_integer::detail { -RC_GTEST_PROP(storage, assigned_value_equals_original, (storage<128> a)) { - assigned_value_equals_original<128>(a); - assigned_value_equals_original<256>(a); -} - -RC_GTEST_PROP(storage, copied_value_equals_original, (storage<128> a)) { - copied_value_equals_original<128>(a); - copied_value_equals_original<256>(a); -} - -RC_GTEST_PROP(storage, get_set_is_consistent, - (uint32_t value, storage<512> a)) { - auto const i = *rc::gen::inRange(0, a.num_elems); - a.set(i, value); - RC_ASSERT(a.get(i) == value); -} - -TEST(storage, default_construction) { - storage<32> s{}; - ASSERT_EQ(s.get(0), 0u); -} - -TEST(storage, num_elems) { - ASSERT_EQ(storage<129>::num_elems, 5); - ASSERT_EQ(storage<128>::num_elems, 4); - ASSERT_EQ(storage<127>::num_elems, 4); - - ASSERT_EQ(storage<97>::num_elems, 4); - ASSERT_EQ(storage<96>::num_elems, 3); - ASSERT_EQ(storage<95>::num_elems, 3); - - ASSERT_EQ(storage<33>::num_elems, 2); - ASSERT_EQ(storage<32>::num_elems, 1); - ASSERT_EQ(storage<1>::num_elems, 1); -} - -TEST(storage, equality) { - ASSERT_EQ(storage<32>{{42u}}, storage<32>{{42u}}); - ASSERT_NE(storage<32>{{42u}}, storage<32>{{13u}}); - - ASSERT_EQ(storage<32>{{0x8000'0000u}}, - (storage<64>{{0x8000'0000, 0xffff'ffffu}})); - ASSERT_NE(storage<32>{{0x8000'0000u}}, - (storage<64>{{0x8000'0000, 0x0000'0000u}})); - ASSERT_EQ(storage<32>{{0x7000'0000u}}, - (storage<64>{{0x7000'0000, 0x0000'0000u}})); - ASSERT_NE(storage<32>{{0x7000'0000u}}, - (storage<64>{{0x7000'0000, 0xffff'ffffu}})); -} - -TEST(storage, in_bound_get_set) { - storage<128> v{}; - - v.set(0, 0xba5e'ba11u); - v.set(1, 0x57ac'ca70u); - v.set(2, 0x1111'1111u); - v.set(3, 0x7777'7777u); - - ASSERT_EQ(v.get(0), 0xba5e'ba11u); - ASSERT_EQ(v.get(1), 0x57ac'ca70u); - ASSERT_EQ(v.get(2), 0x1111'1111u); - ASSERT_EQ(v.get(3), 0x7777'7777u); -} - -TEST(storage, out_bound_positive_get_set) { - storage<128> v{}; - - v.set(4, 0xba5e'ba11u); - v.set(5, 0x57ac'ca70u); - v.set(6, 0x1111'1111u); - v.set(7, 0x7777'7777u); - - ASSERT_EQ(v.get(4), 0u); - ASSERT_EQ(v.get(5), 0u); - ASSERT_EQ(v.get(6), 0u); - ASSERT_EQ(v.get(7), 0u); -} - -TEST(storage, negative) { - storage<128> v{}; - - v.set(3, 0x8000'0000u); - ASSERT_EQ(v.negative(), true); - - v.set(3, 0x7fff'ffffu); - ASSERT_EQ(v.negative(), false); -} - -TEST(storage, out_bound_negative_get_set) { - storage<128> v{}; - - v.set(3, 0x8000'0000u); - v.set(4, 0xba5e'ba11u); - v.set(5, 0x57ac'ca70u); - v.set(6, 0x1111'1111u); - - ASSERT_EQ(v.get(3), 0x8000'0000u); - ASSERT_EQ(v.get(4), 0xffff'ffffu); - ASSERT_EQ(v.get(5), 0xffff'ffffu); - ASSERT_EQ(v.get(6), 0xffff'ffffu); - ASSERT_EQ(v.get(7), 0xffff'ffffu); -} - -TEST(storage, array_constructor) { - storage<128> v{{42u, 43u, 44u, 45u}}; - - ASSERT_EQ(v.get(0), 42u); - ASSERT_EQ(v.get(1), 43u); - ASSERT_EQ(v.get(2), 44u); - ASSERT_EQ(v.get(3), 45u); -} - -TEST(storage, copy_constructor) { - storage<128> v1{{42u, 43u, 44u, 45u}}; - storage<128> v2{v1}; - - ASSERT_EQ(v2.get(0), 42u); - ASSERT_EQ(v2.get(1), 43u); - ASSERT_EQ(v2.get(2), 44u); - ASSERT_EQ(v2.get(3), 45u); -} - -TEST(storage, copy_assignment) { - storage<128> v1{{42u, 43u, 44u, 45u}}; - storage<128> v2{}; - v2 = v1; - - ASSERT_EQ(v2.get(0), 42u); - ASSERT_EQ(v2.get(1), 43u); - ASSERT_EQ(v2.get(2), 44u); - ASSERT_EQ(v2.get(3), 45u); -} - -TEST(storage, assign_to_larger) { - storage<128> v1{{42u, 43u, 44u, 45u}}; - storage<256> v2{}; - v2 = v1; - - ASSERT_EQ(v2.get(0), 42u); - ASSERT_EQ(v2.get(1), 43u); - ASSERT_EQ(v2.get(2), 44u); - ASSERT_EQ(v2.get(3), 45u); - ASSERT_EQ(v2.get(4), 0u); - ASSERT_EQ(v2.get(5), 0u); - ASSERT_EQ(v2.get(6), 0u); - ASSERT_EQ(v2.get(7), 0u); -} -} // namespace safe::_big_integer::detail \ No newline at end of file From b8f0f8deebc1dcf4f294139f5eac54567a910380 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Thu, 21 Mar 2024 12:41:54 +0900 Subject: [PATCH 03/13] remove big_integer usage --- include/safe.hpp | 1 - include/safe/big_integer.hpp | 2 - .../big_integer/interface/big_integer.hpp | 9 +- include/safe/dsl/constrain_interval.hpp | 3 +- include/safe/dsl/constrain_mask.hpp | 10 +- include/safe/dsl/constrain_set.hpp | 3 +- test/safe/big_integer.cpp | 187 ------------------ test/safe/dsl.cpp | 93 ++------- test/safe/dsl/detail/triint.cpp | 28 +-- 9 files changed, 44 insertions(+), 292 deletions(-) delete mode 100644 test/safe/big_integer.cpp diff --git a/include/safe.hpp b/include/safe.hpp index b4e2e54..7de7dc9 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include diff --git a/include/safe/big_integer.hpp b/include/safe/big_integer.hpp index d4ae458..ca81ce3 100644 --- a/include/safe/big_integer.hpp +++ b/include/safe/big_integer.hpp @@ -3,7 +3,5 @@ #include namespace safe { -using _big_integer::interface::big_integer; using _big_integer::interface::common_integer_t; -using _big_integer::interface::to_big_integer; } // namespace safe \ No newline at end of file diff --git a/include/safe/big_integer/interface/big_integer.hpp b/include/safe/big_integer/interface/big_integer.hpp index 1168df0..abe26ef 100644 --- a/include/safe/big_integer/interface/big_integer.hpp +++ b/include/safe/big_integer/interface/big_integer.hpp @@ -94,6 +94,12 @@ template struct big_integer { template struct common_integer; + +template +struct common_integer { + using type = std::common_type_t; +}; + template struct common_integer, big_integer> { using type = big_integer; @@ -330,9 +336,6 @@ template return big_integer{result}; } -[[nodiscard]] constexpr auto to_big_integer(auto const &value) { - return big_integer{detail::to_storage(value)}; -} } // namespace safe::_big_integer::interface template diff --git a/include/safe/dsl/constrain_interval.hpp b/include/safe/dsl/constrain_interval.hpp index b8bdd81..e6b32b2 100644 --- a/include/safe/dsl/constrain_interval.hpp +++ b/include/safe/dsl/constrain_interval.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -21,7 +20,7 @@ template struct constrain_interval_t : public detail::primi }; template -constexpr constrain_interval_t constrain_interval{}; +constexpr constrain_interval_t constrain_interval{}; template constexpr bool is_ival_v = false; diff --git a/include/safe/dsl/constrain_mask.hpp b/include/safe/dsl/constrain_mask.hpp index 78fa921..8f9dbd3 100644 --- a/include/safe/dsl/constrain_mask.hpp +++ b/include/safe/dsl/constrain_mask.hpp @@ -7,7 +7,7 @@ #include namespace safe::dsl { -template +template struct constrain_mask_t : public detail::primitive { using type = constrain_mask_t; @@ -15,13 +15,13 @@ struct constrain_mask_t : public detail::primitive { constexpr static auto const_bits = ConstantBits; constexpr static auto value = triint{var_bits, const_bits}; - [[nodiscard]] SAFE_PURE constexpr static auto check(auto value) -> bool { - return (~var_bits & value) == (~var_bits & const_bits); + [[nodiscard]] SAFE_PURE constexpr static auto check(auto value_arg) -> bool { + return (~var_bits & value_arg) == (~var_bits & const_bits); } }; -template -constexpr constrain_mask_t +template +constexpr constrain_mask_t constrain_mask{}; namespace detail { diff --git a/include/safe/dsl/constrain_set.hpp b/include/safe/dsl/constrain_set.hpp index 6e314cb..f186472 100644 --- a/include/safe/dsl/constrain_set.hpp +++ b/include/safe/dsl/constrain_set.hpp @@ -1,13 +1,12 @@ #pragma once -#include #include #include namespace safe::dsl { template using constrain_set_t = - union_t...>; + union_t...>; template constexpr constrain_set_t constrain_set{}; } // namespace safe::dsl \ No newline at end of file diff --git a/test/safe/big_integer.cpp b/test/safe/big_integer.cpp deleted file mode 100644 index 4a02336..0000000 --- a/test/safe/big_integer.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "big_integer/detail/properties.hpp" - -#include -#include - -#include -#include - -namespace safe { -RC_GTEST_PROP(big_integer, is_plus_64, (int64_t a, int64_t b)) { - RC_PRE(_big_integer::detail::addition_will_not_overflow(a, b)); - RC_ASSERT(big_integer<64>(a + b) == - big_integer<64>(big_integer<64>(a) + big_integer<64>(b))); -} - -RC_GTEST_PROP(big_integer, is_minus_64, (int64_t a, int64_t b)) { - RC_PRE(_big_integer::detail::subtraction_will_not_overflow(a, b)); - RC_ASSERT(big_integer<64>(a - b) == - big_integer<64>(big_integer<64>(a) - big_integer<64>(b))); -} - -RC_GTEST_PROP(big_integer, is_multiply_64, (int32_t a, int32_t b)) { - auto a64 = static_cast(a); - auto b64 = static_cast(b); - RC_ASSERT(big_integer<64>(a64 * b64) == - big_integer<64>(big_integer<64>(a64) * big_integer<64>(b64))); -} - -RC_GTEST_PROP(big_integer, is_divide_64, (int64_t a, int64_t b)) { - RC_PRE(b != 0); - RC_ASSERT(big_integer<64>(a / b) == - big_integer<64>(big_integer<64>(a) / big_integer<64>(b))); -} - -RC_GTEST_PROP(big_integer, is_mod_64, (int64_t a, int64_t b)) { - RC_PRE(b != 0); - RC_ASSERT(big_integer<64>(a % b) == - big_integer<64>(big_integer<64>(a) % big_integer<64>(b))); -} - -RC_GTEST_PROP(big_integer, is_bit_or_64, (int64_t a, int64_t b)) { - RC_ASSERT(big_integer<64>(a | b) == - (big_integer<64>(a) | big_integer<64>(b))); -} - -RC_GTEST_PROP(big_integer, is_bit_and_64, (int64_t a, int64_t b)) { - RC_ASSERT(big_integer<64>(a & b) == - (big_integer<64>(a) & big_integer<64>(b))); -} - -RC_GTEST_PROP(big_integer, is_bit_xor_64, (int64_t a, int64_t b)) { - RC_ASSERT(big_integer<64>(a ^ b) == - (big_integer<64>(a) ^ big_integer<64>(b))); -} - -RC_GTEST_PROP(big_integer, is_bit_shift_left_64, (int64_t a)) { - auto const i = *rc::gen::inRange(0, 63); - RC_ASSERT(big_integer<64>(a << i) == (big_integer<64>(a) << i)); -} - -RC_GTEST_PROP(big_integer, is_bit_shift_right_64, (int64_t a)) { - auto const i = *rc::gen::inRange(0, 63); - RC_ASSERT(big_integer<64>(a >> i) == (big_integer<64>(a) >> i)); -} - -RC_GTEST_PROP(big_integer, plus_is_commutative, - (big_integer<256> a, big_integer<256> b)) { - RC_ASSERT((a + b) == (b + a)); -} - -RC_GTEST_PROP(big_integer, plus_is_associative, - (big_integer<256> a, big_integer<256> b, big_integer<256> c)) { - RC_ASSERT(((a + b) + c) == (a + (b + c))); -} - -RC_GTEST_PROP(big_integer, plus_identity, (big_integer<256> a)) { - RC_ASSERT((a + 0) == a); -} - -RC_GTEST_PROP(big_integer, minus_itself_is_zero, (big_integer<256> a)) { - RC_ASSERT((a - a) == 0); -} - -RC_GTEST_PROP(big_integer, multiply_is_commutative, - (big_integer<256> a, big_integer<256> b)) { - RC_ASSERT((a * b) == (b * a)); -} - -RC_GTEST_PROP(big_integer, multiply_is_associative, - (big_integer<256> a, big_integer<256> b, big_integer<256> c)) { - RC_ASSERT(((a * b) * c) == (a * (b * c))); -} - -RC_GTEST_PROP(big_integer, multiply_identity, (big_integer<256> a)) { - RC_ASSERT((a * 1) == a); -} - -RC_GTEST_PROP(big_integer, multiply_by_zero_is_zero, (big_integer<256> a)) { - RC_ASSERT((a * 0) == 0); -} - -RC_GTEST_PROP(big_integer, divmod_and_multiply_relation, - (big_integer<256> dividend, big_integer<256> divisor)) { - RC_PRE(divisor != 0); - auto [q, r] = divmod(dividend, divisor); - RC_ASSERT(((divisor * q) + r) == dividend); -} - -RC_GTEST_PROP(big_integer, division_remainder_and_multiply_relation, - (big_integer<256> dividend, big_integer<256> divisor)) { - RC_PRE(divisor != 0); - RC_ASSERT(((divisor * (dividend / divisor)) + (dividend % divisor)) == - dividend); -} - -RC_GTEST_PROP(big_integer, division_identity, (big_integer<256> a)) { - RC_ASSERT((a / 1) == a); -} - -RC_GTEST_PROP(big_integer, division_zero_dividend, (big_integer<256> a)) { - RC_PRE(a != 0); - RC_ASSERT((0 / a) == 0); -} - -RC_GTEST_PROP(big_integer, shift_left_and_right, (big_integer<256> a)) { - big_integer<512> b{a}; - - auto const i = *rc::gen::inRange(0, 256); - RC_ASSERT(b == ((b << i) >> i)); -} - -RC_GTEST_PROP(big_integer, shift_distributes_over_add, - (big_integer<256> a, big_integer<256> b)) { - big_integer<512> a_prime{a}; - big_integer<512> b_prime{b}; - - auto const i = *rc::gen::inRange(0, 256); - - RC_ASSERT(((a_prime + b_prime) << i) == ((a_prime << i) + (b_prime << i))); -} - -RC_GTEST_PROP(big_integer, bitwise_demorgan_laws, - (big_integer<512> a, big_integer<512> b)) { - RC_ASSERT(~(a | b) == (~a & ~b)); - RC_ASSERT(~(a & b) == (~a | ~b)); -} - -void shifted_operation(auto op, big_integer<64> small_a, - big_integer<64> small_b) { - big_integer<1024> a{small_a}; - big_integer<1024> b{small_b}; - - auto const i = *rc::gen::inRange(0, 959); - - RC_ASSERT(op(a, b) == op((a << i), (b << i)) >> i); -} - -RC_GTEST_PROP(big_integer, shifted_bit_and, - (big_integer<64> small_a, big_integer<64> small_b)) { - shifted_operation(std::bit_and{}, small_a, small_b); -} - -RC_GTEST_PROP(big_integer, shifted_bit_or, - (big_integer<64> small_a, big_integer<64> small_b)) { - shifted_operation(std::bit_or{}, small_a, small_b); -} - -RC_GTEST_PROP(big_integer, shifted_bit_xor, - (big_integer<64> small_a, big_integer<64> small_b)) { - shifted_operation(std::bit_xor{}, small_a, small_b); -} - -RC_GTEST_PROP(big_integer, shifted_plus, - (big_integer<64> small_a, big_integer<64> small_b)) { - shifted_operation(std::plus{}, small_a, small_b); -} - -RC_GTEST_PROP(big_integer, shifted_minus, - (big_integer<64> small_a, big_integer<64> small_b)) { - shifted_operation(std::minus{}, small_a, small_b); -} - -TEST(big_integer, plus) { - using T = big_integer<32>; - ASSERT_EQ(T(1) + T(2), T(3)); -} -} // namespace safe diff --git a/test/safe/dsl.cpp b/test/safe/dsl.cpp index 6f931d8..9df759f 100644 --- a/test/safe/dsl.cpp +++ b/test/safe/dsl.cpp @@ -40,65 +40,6 @@ constexpr void test_operation() { run_tests(mp_product{}); } -template struct modulo_test_op { - constexpr static LhsT lhs{}; - constexpr static RhsT rhs{}; - - constexpr static void run() { - constexpr bool rhs_nonzero = - (rhs.min < 0 && rhs.max < 0) || (rhs.min > 0 && rhs.max > 0); - - if constexpr (rhs_nonzero) { - check_interval>( - lhs, rhs, [](auto a, auto b) { return a % b; }); - } - } -}; - -template struct divide_test_op { - constexpr static LhsT lhs{}; - constexpr static RhsT rhs{}; - - constexpr static void run() { - constexpr bool rhs_nonzero = - (rhs.min < 0 && rhs.max < 0) || (rhs.min > 0 && rhs.max > 0); - - if constexpr (rhs_nonzero) { - check_interval>( - lhs, rhs, [](auto a, auto b) { return a / b; }); - } - } -}; - -template struct add_test_op { - constexpr static LhsT lhs{}; - constexpr static RhsT rhs{}; - - constexpr static void run() { - check_interval>( - lhs, rhs, [](auto a, auto b) { return a + b; }); - } -}; - -template struct minus_test_op { - constexpr static LhsT lhs{}; - constexpr static RhsT rhs{}; - - constexpr static void run() { - check_interval>( - lhs, rhs, [](auto a, auto b) { return a - b; }); - } -}; - -template struct multiply_test_op { - constexpr static LhsT lhs{}; - constexpr static RhsT rhs{}; - - constexpr static void run() { - check_interval>( - lhs, rhs, [](auto a, auto b) { return a * b; }); - } -}; template using operands = @@ -108,21 +49,21 @@ using operands = constrain_interval_t<1 * scale, 3 * scale>, constrain_interval_t<2 * scale, 4 * scale>, constrain_interval_t<3 * scale, 5 * scale>>; -TEST(safe_dsl_test, add_op) { - test_operation, operands<5>>(); -} +// TEST(safe_dsl_test, add_op) { +// test_operation, operands<5>>(); +// } -TEST(safe_dsl_test, minus_op) { - test_operation, operands<1>>(); -} +// TEST(safe_dsl_test, minus_op) { +// test_operation, operands<1>>(); +// } -TEST(safe_dsl_test, multiply_op) { - test_operation, operands<5>>(); -} +// TEST(safe_dsl_test, multiply_op) { +// test_operation, operands<5>>(); +// } -TEST(safe_dsl_test, divide_op) { - test_operation, operands<5>>(); -} +// TEST(safe_dsl_test, divide_op) { +// test_operation, operands<5>>(); +// } // template // struct show { @@ -142,7 +83,7 @@ TEST(safe_dsl_test, modulo_op) { EXPECT_EQ((constrain_interval<-1000, -100> % constrain_interval<100, 1000>), (constrain_interval<-900, 0> || constrain_interval<-999, -100>)); - EXPECT_EQ((constrain_interval<-1000, -101> % constrain_interval<1, 100>), (constrain_interval<-99, 0>)); + // EXPECT_EQ((constrain_interval<-1000, -101> % constrain_interval<1, 100>), (constrain_interval<-99, 0>)); // test_operation< // modulo_test_op, // operands<5>, @@ -276,9 +217,9 @@ TEST(safe_dsl_test, mask_shift_left) { } TEST(safe_dsl_test, mask_shift_right) { - EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<0, 0>), constrain_mask<0b1000>); - EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<1, 1>), constrain_mask<0b0100>); + // EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<0, 0>), constrain_mask<0b1000>); + // EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<1, 1>), constrain_mask<0b0100>); - EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<0, 1>), constrain_mask<0b1100>); - EXPECT_EQ((constrain_mask<0b10000000> >> constrain_interval<4, 6>), constrain_mask<0b1110>); + // EXPECT_EQ((constrain_mask<0b1000> >> constrain_interval<0, 1>), constrain_mask<0b1100>); + // EXPECT_EQ((constrain_mask<0b10000000> >> constrain_interval<4, 6>), constrain_mask<0b1110>); } diff --git a/test/safe/dsl/detail/triint.cpp b/test/safe/dsl/detail/triint.cpp index dcc75e7..dece909 100644 --- a/test/safe/dsl/detail/triint.cpp +++ b/test/safe/dsl/detail/triint.cpp @@ -7,21 +7,21 @@ #include namespace safe::dsl::detail { -template using big_triint = triint>; +// template using big_triint = triint>; -RC_GTEST_PROP(triint, bit_and_is_commutative, - (big_triint<256> a, big_triint<256> b)) { - RC_ASSERT((a & b) == (b & a)); -} +// RC_GTEST_PROP(triint, bit_and_is_commutative, +// (big_triint<256> a, big_triint<256> b)) { +// RC_ASSERT((a & b) == (b & a)); +// } -RC_GTEST_PROP(triint, bit_and_is_associative, - (big_triint<256> a, big_triint<256> b, big_triint<256> c)) { - RC_ASSERT(((a & b) & c) == (a & (b & c))); -} +// RC_GTEST_PROP(triint, bit_and_is_associative, +// (big_triint<256> a, big_triint<256> b, big_triint<256> c)) { +// RC_ASSERT(((a & b) & c) == (a & (b & c))); +// } -RC_GTEST_PROP(triint, bitwise_demorgan_laws, - (big_triint<256> a, big_triint<256> b)) { - RC_ASSERT(~(a | b) == (~a & ~b)); - RC_ASSERT(~(a & b) == (~a | ~b)); -} +// RC_GTEST_PROP(triint, bitwise_demorgan_laws, +// (big_triint<256> a, big_triint<256> b)) { +// RC_ASSERT(~(a | b) == (~a & ~b)); +// RC_ASSERT(~(a & b) == (~a | ~b)); +// } } // namespace safe::dsl::detail \ No newline at end of file From 1c7c4f6335cbef90ea7efd8f4b1d6f214b61c044 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Thu, 21 Mar 2024 13:01:44 +0900 Subject: [PATCH 04/13] removed big_integer --- include/safe/big_integer.hpp | 4 +- .../safe/big_integer/detail/algorithms.hpp | 42 -- include/safe/big_integer/detail/bitwise.hpp | 13 - include/safe/big_integer/detail/compare.hpp | 43 -- include/safe/big_integer/detail/divides.hpp | 96 ----- .../safe/big_integer/detail/multiplies.hpp | 62 --- include/safe/big_integer/detail/operators.hpp | 8 - include/safe/big_integer/detail/plus.hpp | 32 -- include/safe/big_integer/detail/shift.hpp | 69 --- include/safe/big_integer/detail/storage.hpp | 171 -------- .../big_integer/interface/big_integer.hpp | 393 ------------------ include/safe/big_integer/interface/fwd.hpp | 7 - include/safe/common_integer.hpp | 20 + include/safe/dsl/constrain_mask.hpp | 1 - include/safe/dsl/detail/eval_union.hpp | 2 +- include/safe/dsl/detail/triint.hpp | 2 +- include/safe/dsl/modulo.hpp | 1 - test/safe/big_integer/detail/properties.hpp | 96 ----- test/safe/big_integer/detail/storage_gen.hpp | 35 -- test/safe/big_integer_gen.hpp | 34 -- test/safe/constrained_number.cpp | 2 - test/safe/dsl.cpp | 2 - test/safe/dsl/add.cpp | 2 - test/safe/dsl/detail/triint.cpp | 30 +- test/safe/dsl/detail/triint_gen.hpp | 3 + 25 files changed, 41 insertions(+), 1129 deletions(-) delete mode 100644 include/safe/big_integer/detail/algorithms.hpp delete mode 100644 include/safe/big_integer/detail/bitwise.hpp delete mode 100644 include/safe/big_integer/detail/compare.hpp delete mode 100644 include/safe/big_integer/detail/divides.hpp delete mode 100644 include/safe/big_integer/detail/multiplies.hpp delete mode 100644 include/safe/big_integer/detail/operators.hpp delete mode 100644 include/safe/big_integer/detail/plus.hpp delete mode 100644 include/safe/big_integer/detail/shift.hpp delete mode 100644 include/safe/big_integer/detail/storage.hpp delete mode 100644 include/safe/big_integer/interface/big_integer.hpp delete mode 100644 include/safe/big_integer/interface/fwd.hpp create mode 100644 include/safe/common_integer.hpp delete mode 100644 test/safe/big_integer/detail/properties.hpp delete mode 100644 test/safe/big_integer/detail/storage_gen.hpp delete mode 100644 test/safe/big_integer_gen.hpp diff --git a/include/safe/big_integer.hpp b/include/safe/big_integer.hpp index ca81ce3..6495a95 100644 --- a/include/safe/big_integer.hpp +++ b/include/safe/big_integer.hpp @@ -1,7 +1,7 @@ #pragma once -#include + namespace safe { -using _big_integer::interface::common_integer_t; +//using _big_integer::interface::common_integer_t; } // namespace safe \ No newline at end of file diff --git a/include/safe/big_integer/detail/algorithms.hpp b/include/safe/big_integer/detail/algorithms.hpp deleted file mode 100644 index 7d153e9..0000000 --- a/include/safe/big_integer/detail/algorithms.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -#include - -namespace safe::_big_integer::detail { -[[nodiscard]] constexpr auto reverse_zip_transform(auto op) { - return [=](auto &result, auto const &lhs, auto const &rhs) -> void { - for (auto i = result.num_elems; i > std::size_t{};) { - --i; - result.set(i, op(lhs.get(i), rhs.get(i))); - } - }; -} - -[[nodiscard]] constexpr auto zip_transform(auto op) { - return [=](auto &result, auto const &lhs, auto const &rhs) -> void { - for (auto i = std::size_t{}; i < result.num_elems; i++) { - result.set(i, op(lhs.get(i), rhs.get(i))); - } - }; -} - -[[nodiscard]] constexpr auto stateful_zip_transform(auto initial_value, - auto op) { - return [=](auto &result, auto const &lhs, auto const &rhs) -> void { - auto state = initial_value; - for (auto i = std::size_t{}; i < result.num_elems; i++) { - result.set(i, op(state, lhs.get(i), rhs.get(i))); - } - }; -} - -[[nodiscard]] constexpr auto transform(auto op) { - return [=](auto &result, auto const &value) -> void { - for (auto i = std::size_t{}; i < result.num_elems; i++) { - result.set(i, op(value.get(i))); - } - }; -} -} // namespace safe::_big_integer::detail diff --git a/include/safe/big_integer/detail/bitwise.hpp b/include/safe/big_integer/detail/bitwise.hpp deleted file mode 100644 index 3e9b613..0000000 --- a/include/safe/big_integer/detail/bitwise.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace safe::_big_integer::detail { -constexpr static auto bit_and = zip_transform(std::bit_and{}); -constexpr static auto bit_or = zip_transform(std::bit_or{}); -constexpr static auto bit_xor = zip_transform(std::bit_xor{}); -constexpr static auto bit_not = transform(std::bit_not{}); -} // namespace safe::_big_integer::detail \ No newline at end of file diff --git a/include/safe/big_integer/detail/compare.hpp b/include/safe/big_integer/detail/compare.hpp deleted file mode 100644 index a887413..0000000 --- a/include/safe/big_integer/detail/compare.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -template -[[nodiscard]] constexpr auto unsigned_compare(storage const &lhs, - storage const &rhs) - -> std::strong_ordering { - for (auto i = std::max(lhs.num_elems, rhs.num_elems); i > std::size_t{};) { - --i; - auto const l = lhs.get(i); - auto const r = rhs.get(i); - if (l < r) { - return std::strong_ordering::less; - } - if (l > r) { - return std::strong_ordering::greater; - } - } - return std::strong_ordering::equal; -} - -template -[[nodiscard]] constexpr auto operator<=>(storage const &lhs, - storage const &rhs) - -> std::strong_ordering { - if (lhs.negative()) { - if (rhs.negative()) { - return unsigned_compare(lhs, rhs); - } - return std::strong_ordering::less; - } - if (rhs.negative()) { - return std::strong_ordering::greater; - } - return unsigned_compare(lhs, rhs); -} -} // namespace safe::_big_integer::detail diff --git a/include/safe/big_integer/detail/divides.hpp b/include/safe/big_integer/detail/divides.hpp deleted file mode 100644 index 5791cc0..0000000 --- a/include/safe/big_integer/detail/divides.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -template -[[nodiscard]] constexpr auto largest_doubling(storage a, - storage b) { - using ret_t = storage; - - auto ret = ret_t{b}; - - auto const not_big_enough = [&]() -> bool { - auto a_prime = a; - minus(a_prime, a_prime, ret); - return ret <= a_prime; - }; - - while (not_big_enough()) { - bit_shift_left(ret, ret, 1); - } - - return ret; -} - -constexpr static auto unsigned_divmod = [](auto "ient, auto &remainder, - auto const ÷nd, - auto const &divisor) -> void { - // NOTE: Algorithm is based on "Elements of Programming" - // section 5.7 Quotient - - auto a = dividend; - auto b = divisor; - - if (a < b) { - quotient = make_storage(0); - remainder = a; - - } else { - constexpr auto one = make_storage(1); - - auto c = largest_doubling(a, b); - minus(a, a, c); - quotient = one; - while (c != b) { - bit_shift_left(quotient, quotient, 1); - bit_shift_right(c, c, 1); - if (c <= a) { - minus(a, a, c); - plus(quotient, quotient, one); - } - } - - remainder = a; - } -}; - -constexpr static auto divmod = [](auto "ient, auto &remainder, - auto const ÷nd, - auto const &divisor) -> void { - auto a = dividend; - auto b = divisor; - - if (a.negative()) { - if (b.negative()) { - negate(a, a); - negate(b, b); - unsigned_divmod(quotient, remainder, a, b); - - } else { - negate(a, a); - unsigned_divmod(quotient, remainder, a, b); - negate(quotient, quotient); - } - - negate(remainder, remainder); - - } else { - if (b.negative()) { - negate(b, b); - unsigned_divmod(quotient, remainder, a, b); - negate(quotient, quotient); - - } else { - unsigned_divmod(quotient, remainder, a, b); - } - } -}; -} // namespace safe::_big_integer::detail diff --git a/include/safe/big_integer/detail/multiplies.hpp b/include/safe/big_integer/detail/multiplies.hpp deleted file mode 100644 index 3f81f44..0000000 --- a/include/safe/big_integer/detail/multiplies.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include - -namespace safe::_big_integer::detail { -constexpr static auto unsigned_multiplies = [](auto &result, auto const &lhs, - auto const &rhs) -> void { - using result_t = std::remove_cvref_t; - - for (int i = 0; i < lhs.num_elems; i++) { - for (int j = 0; j < rhs.num_elems; j++) { - auto const raw_partial_product = - static_cast(lhs.get(i)) * - static_cast(rhs.get(j)); - - result_t partial_product{ - {static_cast(raw_partial_product & 0xffff'ffffu), - static_cast(raw_partial_product >> 32u)}}; - - bit_shift_left(partial_product, partial_product, (i + j) * 32); - - plus(result, result, partial_product); - } - } -}; - -constexpr static auto multiplies = [](auto &result, auto const &lhs, - auto const &rhs) -> void { - using lhs_t = std::remove_cvref_t; - using rhs_t = std::remove_cvref_t; - - if (lhs.negative()) { - lhs_t pos_lhs{}; - negate(pos_lhs, lhs); - - if (rhs.negative()) { - rhs_t pos_rhs{}; - negate(pos_rhs, rhs); - unsigned_multiplies(result, pos_lhs, pos_rhs); - } else { - unsigned_multiplies(result, pos_lhs, rhs); - negate(result, result); - } - } else { - if (rhs.negative()) { - rhs_t pos_rhs{}; - negate(pos_rhs, rhs); - unsigned_multiplies(result, lhs, pos_rhs); - negate(result, result); - - } else { - unsigned_multiplies(result, lhs, rhs); - } - } -}; -} // namespace safe::_big_integer::detail diff --git a/include/safe/big_integer/detail/operators.hpp b/include/safe/big_integer/detail/operators.hpp deleted file mode 100644 index 9073d78..0000000 --- a/include/safe/big_integer/detail/operators.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include diff --git a/include/safe/big_integer/detail/plus.hpp b/include/safe/big_integer/detail/plus.hpp deleted file mode 100644 index c64babc..0000000 --- a/include/safe/big_integer/detail/plus.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace safe::_big_integer::detail { -constexpr static auto plus = - stateful_zip_transform(elem_t{}, [](elem_t &carry, double_elem_t const &lhs, - double_elem_t const &rhs) { - double_elem_t const result = - lhs + rhs + static_cast(carry); - - carry = result >> 32u; - return result & 0xffff'ffffu; - }); - -constexpr static auto negate = [](auto &result, auto const &value) -> void { - std::remove_cvref_t not_value{}; - bit_not(not_value, value); - plus(result, not_value, make_storage(1)); -}; - -constexpr static auto minus = [](auto &result, auto const &lhs, - auto const &rhs) -> void { - std::remove_cvref_t negative_rhs{}; - negate(negative_rhs, rhs); - plus(result, lhs, negative_rhs); -}; -} // namespace safe::_big_integer::detail diff --git a/include/safe/big_integer/detail/shift.hpp b/include/safe/big_integer/detail/shift.hpp deleted file mode 100644 index 24f231c..0000000 --- a/include/safe/big_integer/detail/shift.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -// TODO: can we consolidate the implementations for left and right shift? -namespace safe::_big_integer::detail { -template struct shift_left { - T source; - int32_t shift_amt; - - constexpr shift_left(T source_arg, int32_t shift_amt_arg) - : source{source_arg}, shift_amt{shift_amt_arg} {} - - [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { - return source.get(i - shift_amt); - } -}; - -constexpr static auto bit_shift_left = - [](auto &result, auto const &lhs, - std::signed_integral auto const rhs) -> void { - auto const elem_shift_amt = rhs / 32; - auto const bit_shift_amt = rhs % 32; - - auto const lhs_shifted_upper = shift_left(lhs, elem_shift_amt); - auto const lhs_shifted_lower = shift_left(lhs, elem_shift_amt + 1); - - reverse_zip_transform([=](elem_t const upper, elem_t const lower) { - if (bit_shift_amt == 0) { - return upper; - } - return (upper << bit_shift_amt) | (lower >> (32 - bit_shift_amt)); - })(result, lhs_shifted_upper, lhs_shifted_lower); -}; - -template struct shift_right { - T source; - int32_t shift_amt; - - constexpr shift_right(T source_arg, int32_t shift_amt_arg) - : source{source_arg}, shift_amt{shift_amt_arg} {} - - [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { - return source.get(i + shift_amt); - } -}; - -constexpr static auto bit_shift_right = - [](auto &result, auto const &lhs, - std::signed_integral auto const rhs) -> void { - auto const elem_shift_amt = rhs / 32; - auto const bit_shift_amt = rhs % 32; - - auto const lhs_shifted_upper = shift_right(lhs, elem_shift_amt + 1); - auto const lhs_shifted_lower = shift_right(lhs, elem_shift_amt); - - zip_transform([=](elem_t const upper, elem_t const lower) { - if (bit_shift_amt == 0) { - return lower; - } - return (upper << (32 - bit_shift_amt)) | (lower >> bit_shift_amt); - })(result, lhs_shifted_upper, lhs_shifted_lower); -}; -} // namespace safe::_big_integer::detail diff --git a/include/safe/big_integer/detail/storage.hpp b/include/safe/big_integer/detail/storage.hpp deleted file mode 100644 index 7a9f85b..0000000 --- a/include/safe/big_integer/detail/storage.hpp +++ /dev/null @@ -1,171 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -namespace safe::_big_integer::detail { -using elem_t = std::uint32_t; -using double_elem_t = std::uint64_t; - -template struct storage { - public: - constexpr static auto num_elems = (NumBits + 31) / 32; - constexpr static auto num_bits = NumBits; - - std::array elems{}; - - constexpr storage() = default; - constexpr explicit storage(std::array const &new_elems) - : elems{new_elems} {} - - template - constexpr explicit storage(storage const &rhs) { - for (auto i = std::size_t{}; i < NumBits; i++) { - set(i, rhs.get(i)); - } - }; - - template - constexpr auto operator=(storage const &rhs) -> storage & { - for (auto i = std::size_t{}; i < NumBits; i++) { - set(i, rhs.get(i)); - } - return *this; - }; - - template - [[nodiscard]] constexpr auto - operator==(storage const &rhs) const -> bool { - for (auto i = std::size_t{}; i < std::max(num_elems, rhs.num_elems); - i++) { - if (get(i) != rhs.get(i)) { - return false; - } - } - return true; - } - - [[nodiscard]] constexpr auto negative() const -> bool { - return (elems.back() >> 31) & 1; - } - - [[nodiscard]] constexpr auto get(int32_t i) const -> elem_t { - if (i < 0) { - return 0u; - } - if (i < num_elems) { - return elems[i]; - } - if (negative()) { - return 0xffff'ffffu; - } - return 0u; - } - - constexpr auto set(int32_t i, elem_t elem) -> void { - if (i >= 0 && i < num_elems) { - elems[i] = elem; - } - } -}; - -template - requires(sizeof(T) <= 4 && std::signed_integral>) -[[nodiscard]] constexpr auto to_storage(T v) -> storage { - return storage{ - {static_cast(static_cast(v))}}; -} - -template - requires(sizeof(T) <= 4 && std::unsigned_integral>) -[[nodiscard]] constexpr auto to_storage(T v) -> storage<(sizeof(T) * 8) + 1> { - return storage<(sizeof(T) * 8) + 1>{{static_cast(v)}}; -} - -template - requires(sizeof(T) == 8 && std::signed_integral>) -[[nodiscard]] constexpr auto to_storage(T v) -> storage<64> { - return storage<64>{ - {static_cast(v), static_cast(v >> 32)}}; -} - -template - requires(sizeof(T) == 8 && std::unsigned_integral>) -[[nodiscard]] constexpr auto to_storage(T v) -> storage<65> { - return storage<65>{ - {static_cast(v), static_cast(v >> 32)}}; -} - -template -[[nodiscard]] constexpr auto -to_storage(interface::big_integer const &v) -> auto const & { - return v.unsafe_storage; -} - -template -[[nodiscard]] constexpr auto to_storage(storage &v) -> auto & { - return v; -} - -template -[[nodiscard]] constexpr auto to_storage(storage const &v) - -> auto const & { - return v; -} - -template -[[nodiscard]] constexpr auto to_storage(std::integral_constant) { - return to_storage(value); -} - -[[nodiscard]] constexpr auto make_storage(auto v) { return to_storage(v); } - -[[nodiscard]] constexpr auto to_integral(std::integral auto value) { - return value; -} - -template -[[nodiscard]] constexpr auto to_integral(std::integral_constant) { - return value; -} - -template - requires(NumBits > 32 && NumBits <= 64) -[[nodiscard]] constexpr auto to_integral(storage const &value) - -> int64_t { - return (static_cast(value.get(1)) << 32u) | - (static_cast(value.get(0))); -} - -template - requires(NumBits <= 32) -[[nodiscard]] constexpr auto to_integral(storage const &value) - -> int32_t { - return static_cast(value.get(0)); -} - -template -[[nodiscard]] constexpr auto -to_integral(interface::big_integer const &value) { - return to_integral(value.unsafe_storage); -} - -constexpr static auto max_width_plus_one = - [](std::size_t left_bits, std::size_t right_bits) -> std::size_t { - return std::max(left_bits, right_bits) + 1; -}; - -constexpr static auto max_width = [](std::size_t left_bits, - std::size_t right_bits) -> std::size_t { - return std::max(left_bits, right_bits); -}; - -constexpr static auto sum_width = [](std::size_t left_bits, - std::size_t right_bits) -> std::size_t { - return left_bits + right_bits; -}; -} // namespace safe::_big_integer::detail diff --git a/include/safe/big_integer/interface/big_integer.hpp b/include/safe/big_integer/interface/big_integer.hpp deleted file mode 100644 index abe26ef..0000000 --- a/include/safe/big_integer/interface/big_integer.hpp +++ /dev/null @@ -1,393 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace safe::_big_integer::interface { -template struct big_integer { - detail::storage unsafe_storage{}; - - // NOLINTNEXTLINE(google-explicit-constructor) - constexpr explicit(false) big_integer(auto value) - : unsafe_storage{detail::to_storage(value)} {} - constexpr big_integer() = default; - - constexpr auto operator&=(auto const &rhs) -> big_integer & { - return do_assign_op(detail::bit_and, rhs); - } - - constexpr auto operator|=(auto const &rhs) -> big_integer & { - return do_assign_op(detail::bit_or, rhs); - } - - constexpr auto operator^=(auto const &rhs) -> big_integer & { - return do_assign_op(detail::bit_xor, rhs); - } - - constexpr auto operator+=(auto const &rhs) -> big_integer & { - return do_assign_op(detail::plus, rhs); - } - - constexpr auto operator-=(auto const &rhs) -> big_integer & { - return do_assign_op(detail::minus, rhs); - } - - constexpr auto operator/=(auto const &raw_rhs) -> big_integer & { - auto rhs = detail::to_storage(raw_rhs); - - decltype(unsafe_storage) q{}; - decltype(unsafe_storage) r{}; - - detail::divmod(q, r, unsafe_storage, rhs); - unsafe_storage = q; - - return *this; - } - - constexpr auto operator%=(auto const &raw_rhs) -> big_integer & { - auto rhs = detail::to_storage(raw_rhs); - - decltype(unsafe_storage) q{}; - decltype(unsafe_storage) r{}; - - detail::divmod(q, r, unsafe_storage, rhs); - unsafe_storage = r; - - return *this; - } - - constexpr auto operator<<=(auto const &raw_rhs) -> big_integer & { - auto rhs = detail::to_integral(raw_rhs); - detail::bit_shift_left(unsafe_storage, unsafe_storage, rhs); - return *this; - } - - constexpr auto operator>>=(auto const &raw_rhs) -> big_integer & { - auto rhs = detail::to_integral(raw_rhs); - detail::bit_shift_right(unsafe_storage, unsafe_storage, rhs); - return *this; - } - - constexpr auto operator-() const -> big_integer { - big_integer new_value{}; - detail::negate(new_value.unsafe_storage, unsafe_storage); - return new_value; - } - - constexpr auto operator~() const -> big_integer { - big_integer new_value{}; - detail::bit_not(new_value.unsafe_storage, unsafe_storage); - return new_value; - } - - private: - constexpr auto do_assign_op(auto op, auto const &rhs) -> big_integer & { - op(unsafe_storage, unsafe_storage, detail::to_storage(rhs)); - return *this; - } -}; - -template struct common_integer; - - -template -struct common_integer { - using type = std::common_type_t; -}; - -template -struct common_integer, big_integer> { - using type = big_integer; -}; - -template -struct common_integer> { - using type = big_integer(sizeof(T) * 8, RhsNumBits)>; -}; - -template -struct common_integer> { - using type = - big_integer((sizeof(T) * 8) + 1, RhsNumBits)>; -}; - -template -struct common_integer, T> { - using type = big_integer(sizeof(T) * 8, LhsNumBits)>; -}; - -template -struct common_integer, T> { - using type = - big_integer((sizeof(T) * 8) + 1, LhsNumBits)>; -}; - -template -using common_integer_t = typename common_integer, - std::remove_cvref_t>::type; - -template -constexpr bool at_least_one_big_integer = false; - -template -constexpr bool at_least_one_big_integer, R> = true; - -template -constexpr bool at_least_one_big_integer> = true; - -template -constexpr bool - at_least_one_big_integer, big_integer> = - true; - -template -big_integer(detail::storage) -> big_integer; - -[[nodiscard]] constexpr auto do_binary_op(auto op, auto width_calc, - auto const &raw_lhs, - auto const &raw_rhs) { - auto lhs = detail::to_storage(raw_lhs); - auto rhs = detail::to_storage(raw_rhs); - using lhs_t = std::remove_cvref_t; - using rhs_t = std::remove_cvref_t; - - constexpr auto result_width = width_calc(lhs_t::num_bits, rhs_t::num_bits); - detail::storage result{}; - - op(result, lhs, rhs); - - return big_integer(result); -} - -template -[[nodiscard]] constexpr auto operator==(big_integer const &lhs, - big_integer const &rhs) - -> bool { - return lhs.unsafe_storage == rhs.unsafe_storage; -} - -template -[[nodiscard]] constexpr auto operator==(big_integer const &lhs, - std::integral auto const &rhs) -> bool { - return lhs.unsafe_storage == detail::to_storage(rhs); -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator<=>(L const &raw_lhs, R const &raw_rhs) - -> std::strong_ordering { - auto lhs = detail::to_storage(raw_lhs); - auto rhs = detail::to_storage(raw_rhs); - return lhs <=> rhs; -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator+(L const &raw_lhs, R const &raw_rhs) { - return do_binary_op(detail::plus, detail::max_width_plus_one, raw_lhs, - raw_rhs); -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator-(L const &raw_lhs, R const &raw_rhs) { - return do_binary_op(detail::minus, detail::max_width_plus_one, raw_lhs, - raw_rhs); -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator*(L const &raw_lhs, R const &raw_rhs) { - return do_binary_op(detail::multiplies, detail::sum_width, raw_lhs, - raw_rhs); -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto divmod(L const &raw_lhs, R const &raw_rhs) { - auto lhs = detail::to_storage(raw_lhs); - auto rhs = detail::to_storage(raw_rhs); - using lhs_t = std::remove_cvref_t; - using rhs_t = std::remove_cvref_t; - - lhs_t q{}; - lhs_t r{}; - - detail::divmod(q, r, lhs, rhs); - - return std::make_pair(big_integer{q}, big_integer{r}); -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator/(L const &lhs, R const &rhs) { - auto [q, r] = divmod(lhs, rhs); - return q; -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator%(L const &lhs, R const &rhs) { - auto [q, r] = divmod(lhs, rhs); - return r; -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator|(L const &raw_lhs, R const &raw_rhs) { - return do_binary_op(detail::bit_or, detail::max_width, raw_lhs, raw_rhs); -} - -[[nodiscard]] constexpr auto operator&(auto const &raw_lhs, - auto const &raw_rhs) { - return do_binary_op(detail::bit_and, detail::max_width, raw_lhs, raw_rhs); -} - -template - requires at_least_one_big_integer -[[nodiscard]] constexpr auto operator^(L const &raw_lhs, R const &raw_rhs) { - return do_binary_op(detail::bit_xor, detail::max_width, raw_lhs, raw_rhs); -} - -[[nodiscard]] constexpr auto operator<<(auto const &raw_lhs, - std::integral auto const &rhs) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; - - lhs_t result{}; - - detail::bit_shift_left(result, lhs, rhs); - - return big_integer{result}; -} - -template -[[nodiscard]] constexpr auto operator<<(auto const &raw_lhs, - std::integral_constant) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; - - using result_t = detail::storage; - result_t result{}; - - detail::bit_shift_left(result, lhs, rhs); - - return big_integer{result}; -} - -template -[[nodiscard]] constexpr auto operator<<(auto const &raw_lhs, - big_integer raw_rhs) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; - - auto rhs = detail::to_integral(raw_rhs); - - lhs_t result{}; - - detail::bit_shift_left(result, lhs, rhs); - - return big_integer{result}; -} - -[[nodiscard]] constexpr auto operator>>(auto const &raw_lhs, - std::integral auto const &rhs) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; - - lhs_t result{}; - - detail::bit_shift_right(result, lhs, rhs); - - return big_integer{result}; -} - -template -[[nodiscard]] constexpr auto operator>>(auto const &raw_lhs, - std::integral_constant) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; - - using result_t = detail::storage; - result_t result{}; - - detail::bit_shift_right(result, lhs, rhs); - - return big_integer{result}; -} - -template -[[nodiscard]] constexpr auto operator>>(auto const &raw_lhs, - big_integer raw_rhs) { - auto lhs = detail::to_storage(raw_lhs); - using lhs_t = std::remove_cvref_t; - - auto rhs = detail::to_integral(raw_rhs); - - lhs_t result{}; - - detail::bit_shift_right(result, lhs, rhs); - - return big_integer{result}; -} - -} // namespace safe::_big_integer::interface - -template -struct std::numeric_limits< - safe::_big_integer::interface::big_integer> { - using T = safe::_big_integer::interface::big_integer; - - constexpr static bool is_specialized = true; - constexpr static bool is_signed = true; - constexpr static bool is_integer = true; - constexpr static bool is_exact = true; - constexpr static bool has_infinity = false; - constexpr static bool has_quiet_NaN = false; - constexpr static bool has_signaling_NaN = false; - constexpr static std::float_denorm_style has_denorm = std::denorm_absent; - constexpr static bool has_denorm_loss = false; - constexpr static std::float_round_style round_style = - std::round_toward_zero; - constexpr static bool is_iec559 = false; - constexpr static bool is_bounded = true; - constexpr static bool is_modulo = false; - constexpr static std::size_t digits = - NumBits; // FIXME: the standard says this should be 'int' - constexpr static int digits10 = - std::numeric_limits::digits * std::log10(2); - constexpr static int max_digits10 = 0; - constexpr static int radix = 2; - constexpr static int min_exponent = 0; - constexpr static int min_exponent10 = 0; - constexpr static int max_exponent = 0; - constexpr static int max_exponent10 = 0; - constexpr static bool traps = - true; // NOTE: it _might_ trap for divide by '0' - constexpr static bool tinyness_before = false; - - constexpr static auto min() noexcept -> T { - return T{T{1} << ((T::num_elems * 32) - 1)}; - } - - constexpr static auto lowest() noexcept -> T { return min(); } - - constexpr static auto max() noexcept -> T { return ~min(); } - - constexpr static auto epsilon() noexcept -> T { return T{0}; } - - constexpr static auto round_error() noexcept -> T { return T{0}; } - - constexpr static auto infinity() noexcept -> T { return T{0}; } - - constexpr static auto quiet_NaN() noexcept -> T { return T{0}; } - - constexpr static auto signaling_NaN() noexcept -> T { return T{0}; } - - constexpr static auto denorm_min() noexcept -> T { return T{0}; } -}; diff --git a/include/safe/big_integer/interface/fwd.hpp b/include/safe/big_integer/interface/fwd.hpp deleted file mode 100644 index d756745..0000000 --- a/include/safe/big_integer/interface/fwd.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -namespace safe::_big_integer::interface { -template struct big_integer; -} diff --git a/include/safe/common_integer.hpp b/include/safe/common_integer.hpp new file mode 100644 index 0000000..b2f045d --- /dev/null +++ b/include/safe/common_integer.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace safe { +template struct common_integer; + + +template +struct common_integer { + using type = std::common_type_t; +}; + + +template +using common_integer_t = typename common_integer, + std::remove_cvref_t>::type; + +} \ No newline at end of file diff --git a/include/safe/dsl/constrain_mask.hpp b/include/safe/dsl/constrain_mask.hpp index 8f9dbd3..e50d15b 100644 --- a/include/safe/dsl/constrain_mask.hpp +++ b/include/safe/dsl/constrain_mask.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/include/safe/dsl/detail/eval_union.hpp b/include/safe/dsl/detail/eval_union.hpp index ed3c602..599d272 100644 --- a/include/safe/dsl/detail/eval_union.hpp +++ b/include/safe/dsl/detail/eval_union.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/include/safe/dsl/detail/triint.hpp b/include/safe/dsl/detail/triint.hpp index ae26b49..50e9f99 100644 --- a/include/safe/dsl/detail/triint.hpp +++ b/include/safe/dsl/detail/triint.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/include/safe/dsl/modulo.hpp b/include/safe/dsl/modulo.hpp index 31f9261..585438f 100644 --- a/include/safe/dsl/modulo.hpp +++ b/include/safe/dsl/modulo.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/test/safe/big_integer/detail/properties.hpp b/test/safe/big_integer/detail/properties.hpp deleted file mode 100644 index c377d32..0000000 --- a/test/safe/big_integer/detail/properties.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include -#include -#include -#include - -namespace safe::_big_integer::detail { -template -void is_commutative(auto op, storage a, storage b) { - using result_storage = storage; - result_storage r0{}; - op(r0, a, b); - result_storage r1{}; - op(r1, b, a); - RC_ASSERT(r0 == r1); -} - -template -void is_associative(auto op, auto a, auto b, auto c) { - ResultType r0{}; - op(r0, a, b); - ResultType r1{}; - op(r1, r0, c); - - ResultType r2{}; - op(r2, b, c); - ResultType r3{}; - op(r3, a, r2); - RC_ASSERT(r3 == r1); -} - -template void is_idempotent(auto op, storage a) { - storage result{}; - op(result, a, a); - RC_ASSERT(a == result); -} - -template -void identity(auto op, storage a) { - storage result{}; - op(result, a, id); - RC_ASSERT(a == result); -} - -template -void domination(auto op, storage a) { - storage result{}; - op(result, a, val); - RC_ASSERT(val == result); -} - -template -void assigned_value_equals_original(storage a) { - storage result{}; - result = a; - RC_ASSERT(result == a); -} - -template -void copied_value_equals_original(storage a) { - storage result{a}; - RC_ASSERT(result == a); -} - -template void lhs_can_be_result(auto op, auto a, auto b) { - ResultType result0{}; - op(result0, a, b); - ResultType result1{a}; - op(result1, result1, b); - RC_ASSERT(result0 == result1); -} - -template constexpr auto negation_will_not_overflow(T a) -> bool { - return a != std::numeric_limits::min(); -} - -template -constexpr auto addition_will_not_overflow(T a, T b) -> bool { - return std::signbit(a) != std::signbit(b) or - (a >= 0 and a <= std::numeric_limits::max() - b) or - (a < 0 and a >= std::numeric_limits::min() - b); -} - -template -constexpr auto subtraction_will_not_overflow(T a, T b) -> bool { - return std::signbit(a) == std::signbit(b) or - (a >= 0 and a <= std::numeric_limits::max() + b) or - (a < 0 and a >= std::numeric_limits::min() + b); -} -} // namespace safe::_big_integer::detail diff --git a/test/safe/big_integer/detail/storage_gen.hpp b/test/safe/big_integer/detail/storage_gen.hpp deleted file mode 100644 index 9e11213..0000000 --- a/test/safe/big_integer/detail/storage_gen.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include - -namespace safe::_big_integer::detail { -template -std::ostream &operator<<(std::ostream &os, storage const &s) { - auto i = s.num_elems; - do { - i--; - os << std::setfill('0') << std::hex << std::setw(8) << s.get(i) << " "; - } while (i > 0); - return os; -} -} // namespace safe::_big_integer::detail - -namespace rc { -template -using safe_storage = safe::_big_integer::detail::storage; - -template struct Arbitrary> { - using T = safe_storage; - static Gen arbitrary() { - using src_array = std::array; - - return gen::map(gen::arbitrary(), - [](src_array const &values) { return T{values}; }); - } -}; -} // namespace rc diff --git a/test/safe/big_integer_gen.hpp b/test/safe/big_integer_gen.hpp deleted file mode 100644 index 31455f0..0000000 --- a/test/safe/big_integer_gen.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace safe::_big_integer::interface { -template -std::ostream &operator<<(std::ostream &os, big_integer const &b) { - auto const &s = b.unsafe_storage; - auto i = s.num_elems; - do { - i--; - os << std::setfill('0') << std::hex << std::setw(8) << s.get(i) << " "; - } while (i > 0); - return os; -} -} // namespace safe::_big_integer::interface - -namespace rc { -template -using safe_big_integer = safe::_big_integer::interface::big_integer; - -template struct Arbitrary> { - using T = safe_big_integer; - static Gen arbitrary() { - using src = safe::_big_integer::detail::storage; - - return gen::map(gen::arbitrary(), - [](src const &v) { return T{v}; }); - } -}; -} // namespace rc \ No newline at end of file diff --git a/test/safe/constrained_number.cpp b/test/safe/constrained_number.cpp index e036119..d1ceabc 100644 --- a/test/safe/constrained_number.cpp +++ b/test/safe/constrained_number.cpp @@ -76,7 +76,6 @@ TYPED_TEST(safe_var_ops_test, divide_op) { // EXPECT_EQ(result.unsafe_value(), 2); //} -// FIXME: big_integer needs variable left shift support // TYPED_TEST(safe_var_ops_test, left_shift_op) { // safe::constrained_number, TypeParam> const a = 8_s32; // safe::constrained_number, TypeParam> const b = 2_s32; @@ -84,7 +83,6 @@ TYPED_TEST(safe_var_ops_test, divide_op) { // EXPECT_EQ(result.unsafe_value(), 32); //} -// FIXME: big_integer needs variable right shift support // TYPED_TEST(safe_var_ops_test, right_shift_op) { // safe::constrained_number, TypeParam> const a = 48_s32; // safe::constrained_number, TypeParam> const b = 2_s32; diff --git a/test/safe/dsl.cpp b/test/safe/dsl.cpp index 9df759f..95933cb 100644 --- a/test/safe/dsl.cpp +++ b/test/safe/dsl.cpp @@ -1,8 +1,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include - #include using ::testing::_; diff --git a/test/safe/dsl/add.cpp b/test/safe/dsl/add.cpp index ff54cdc..66a3793 100644 --- a/test/safe/dsl/add.cpp +++ b/test/safe/dsl/add.cpp @@ -1,8 +1,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include - #include using ::testing::_; diff --git a/test/safe/dsl/detail/triint.cpp b/test/safe/dsl/detail/triint.cpp index dece909..805b9ad 100644 --- a/test/safe/dsl/detail/triint.cpp +++ b/test/safe/dsl/detail/triint.cpp @@ -1,5 +1,3 @@ -#include -#include #include #include @@ -7,21 +5,21 @@ #include namespace safe::dsl::detail { -// template using big_triint = triint>; +using big_triint = triint; -// RC_GTEST_PROP(triint, bit_and_is_commutative, -// (big_triint<256> a, big_triint<256> b)) { -// RC_ASSERT((a & b) == (b & a)); -// } +RC_GTEST_PROP(triint, bit_and_is_commutative, + (big_triint a, big_triint b)) { + RC_ASSERT((a & b) == (b & a)); +} -// RC_GTEST_PROP(triint, bit_and_is_associative, -// (big_triint<256> a, big_triint<256> b, big_triint<256> c)) { -// RC_ASSERT(((a & b) & c) == (a & (b & c))); -// } +RC_GTEST_PROP(triint, bit_and_is_associative, + (big_triint a, big_triint b, big_triint c)) { + RC_ASSERT(((a & b) & c) == (a & (b & c))); +} -// RC_GTEST_PROP(triint, bitwise_demorgan_laws, -// (big_triint<256> a, big_triint<256> b)) { -// RC_ASSERT(~(a | b) == (~a & ~b)); -// RC_ASSERT(~(a & b) == (~a | ~b)); -// } +RC_GTEST_PROP(triint, bitwise_demorgan_laws, + (big_triint a, big_triint b)) { + RC_ASSERT(~(a | b) == (~a & ~b)); + RC_ASSERT(~(a & b) == (~a | ~b)); +} } // namespace safe::dsl::detail \ No newline at end of file diff --git a/test/safe/dsl/detail/triint_gen.hpp b/test/safe/dsl/detail/triint_gen.hpp index 20c2f58..2528d39 100644 --- a/test/safe/dsl/detail/triint_gen.hpp +++ b/test/safe/dsl/detail/triint_gen.hpp @@ -2,8 +2,11 @@ #include +#include #include +#include + namespace safe::dsl::detail { template std::ostream &operator<<(std::ostream &os, triint const &t) { From 9e0d0e82eae868c84a7fd3c4cbf3b317cdcd34ff Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 06:58:02 +0900 Subject: [PATCH 05/13] provide a single _cn literal type to create constant constrained_numbers --- README.md | 12 ++-- docs/api_reference.adoc | 10 ++-- include/safe.hpp | 1 - include/safe/big_integer.hpp | 7 --- include/safe/constrained_number.hpp | 2 +- include/safe/contracts.hpp | 5 -- include/safe/int.hpp | 31 +--------- include/safe/object.hpp | 18 ------ test/safe/array.cpp | 12 ++-- .../array_constant_access_max_violation.cpp | 2 +- test/safe/constrained_number.cpp | 60 +++++++++---------- .../assign_constant_max_violation.cpp | 4 +- .../assign_constant_min_violation.cpp | 4 +- .../assign_lhs_rhs_union.cpp | 4 +- .../constrained_number/assign_lhs_union.cpp | 4 +- .../constrained_number/assign_rhs_union.cpp | 4 +- .../construct_constant_max_violation.cpp | 2 +- .../construct_constant_min_violation.cpp | 2 +- .../interval_larger_than_type.cpp | 2 +- test/safe/match.cpp | 42 ++++++------- 20 files changed, 85 insertions(+), 143 deletions(-) delete mode 100644 include/safe/big_integer.hpp delete mode 100644 include/safe/contracts.hpp delete mode 100644 include/safe/object.hpp diff --git a/README.md b/README.md index dfd62b8..06d0684 100644 --- a/README.md +++ b/README.md @@ -51,20 +51,20 @@ using namespace safe::literals; to assign a value to this variable outside this range. ```c++ -ival_s32<0, 1023> enqueue_index = 0_s32; +ival_s32<0, 1023> enqueue_index = 0_cn; ``` We can perform arithmetic operations and prove we will not overflow. If we try to just add '1' to enqueue_index, we could overflow ```c++ -enqueue_index = enqueue_index + 1_s32; // <- COMPILE ERROR +enqueue_index = enqueue_index + 1_cn; // <- COMPILE ERROR ``` Instead, we are required to keep the value in-bounds, in this case we choose to use the modulo operator. ```c++ -enqueue_index = (enqueue_index + 1_s32) % 1024_s32; // GOOD! +enqueue_index = (enqueue_index + 1_cn) % 1024_cn; // GOOD! ``` for cases in which we must index into an array, the *safe arithmetic* library @@ -86,7 +86,7 @@ The `_u32` user-defined literal creates a `safe::constrained_number` type at com is constrained to the single value given to it. ```c++ -auto result = queue_data[4_u32]; // GOOD! +auto result = queue_data[4_cn]; // GOOD! ``` Arithmetic operations generate a new constraint for the result-type. Adding `1` @@ -94,7 +94,7 @@ to `enqueue_index` means it could be one larger than the last element of the array. The *safe arithmetic* library correctly produces a compilation error. ```c++ -auto result = queue_data[enqueue_index + 1_u32]; // <- COMPILE ERROR +auto result = queue_data[enqueue_index + 1_cn]; // <- COMPILE ERROR ``` We must prove to the `safe::array` that the index value is within bounds. @@ -102,7 +102,7 @@ There are many ways to do this. For this queue implementation we want `enqueue_index` to wrap around. ```c++ -auto result = queue_data[(enqueue_index + 1_u32) % 1024_u32]; // GOOD! +auto result = queue_data[(enqueue_index + 1_cn) % 1024_cn]; // GOOD! ``` The `safe::array` advertises this constraint on the `safe::constrained_number` index diff --git a/docs/api_reference.adoc b/docs/api_reference.adoc index 0f739f3..174fe0f 100644 --- a/docs/api_reference.adoc +++ b/docs/api_reference.adoc @@ -546,13 +546,13 @@ auto char_to_ord = safe::match( return dec_digit - s32_<'0'>; }, [](ival_s32<'a', 'f'> lower_hex_digit){ - return lower_hex_digit - s32_<'a'> + 10_s32; + return lower_hex_digit - s32_<'a'> + 10_cn; }, [](ival_s32<'A', 'F'> upper_hex_digit){ - return upper_hex_digit - s32_<'A'> + 10_s32; + return upper_hex_digit - s32_<'A'> + 10_cn; }, [](){ - return 0_s32; + return 0_cn; } ); ``` @@ -602,11 +602,11 @@ value. safe::array values{}; // A bare for-loop will not work. See above discussion for explanation. -// for (safe::ival_s32<0, 9> i = 0_s32; i < 10_s32; i = i + 1_s32) { +// for (safe::ival_s32<0, 9> i = 0_cn; i < 10_cn; i = i + 1_cn) { // :'( // } -for (auto i : safe::irange(0_s32, 10_s32)) { +for (auto i : safe::irange(0_cn, 10_cn)) { // 'i' is proven to be one of 0 through 9... static_assert(std::is_same_v>); diff --git a/include/safe.hpp b/include/safe.hpp index 7de7dc9..0fee4a8 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -6,6 +6,5 @@ #include #include #include -#include #include #include diff --git a/include/safe/big_integer.hpp b/include/safe/big_integer.hpp deleted file mode 100644 index 6495a95..0000000 --- a/include/safe/big_integer.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - - - -namespace safe { -//using _big_integer::interface::common_integer_t; -} // namespace safe \ No newline at end of file diff --git a/include/safe/constrained_number.hpp b/include/safe/constrained_number.hpp index 42f0cc1..f894e43 100644 --- a/include/safe/constrained_number.hpp +++ b/include/safe/constrained_number.hpp @@ -36,7 +36,7 @@ template struct constrained_number { template requires(std::integral && - (constraint >= detail::integral_type::constraint)) + (constraint >= dsl::constraint_of)) // NOLINTNEXTLINE(google-explicit-constructor) SAFE_INLINE constexpr constrained_number(U rhs) : _raw_value{rhs} {} diff --git a/include/safe/contracts.hpp b/include/safe/contracts.hpp deleted file mode 100644 index 1853035..0000000 --- a/include/safe/contracts.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#define SAFE_PRECONDITION(expr) -#define SAFE_INVARIANT(expr) -#define SAFE_POSTCONDITION(expr) \ No newline at end of file diff --git a/include/safe/int.hpp b/include/safe/int.hpp index 87c6498..e859b4f 100644 --- a/include/safe/int.hpp +++ b/include/safe/int.hpp @@ -61,6 +61,7 @@ namespace detail { template [[nodiscard]] constexpr auto to_constant() { // FIXME: handle or fail at compile-time for invalid strings + // FIXME: select correct type ahead of time constexpr T value = []() { constexpr std::array chars{Chars...}; T sum = 0; @@ -78,36 +79,8 @@ template } // namespace detail namespace literals { -template constexpr auto operator""_s8() { - return safe::detail::to_constant(); -} - -template constexpr auto operator""_u8() { - return safe::detail::to_constant(); -} - -template constexpr auto operator""_s16() { - return safe::detail::to_constant(); -} - -template constexpr auto operator""_u16() { - return safe::detail::to_constant(); -} - -template constexpr auto operator""_s32() { - return safe::detail::to_constant(); -} - -template constexpr auto operator""_u32() { - return safe::detail::to_constant(); -} - -template constexpr auto operator""_s64() { +template constexpr auto operator""_cn() { return safe::detail::to_constant(); } - -template constexpr auto operator""_u64() { - return safe::detail::to_constant(); -} } // namespace literals } // namespace safe \ No newline at end of file diff --git a/include/safe/object.hpp b/include/safe/object.hpp deleted file mode 100644 index 015453a..0000000 --- a/include/safe/object.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -namespace safe { -template struct object { - T value; - - [[nodiscard]] auto operator->() -> T * { return &value; } - - [[nodiscard]] auto operator*() -> T & { return value; } -}; - -template struct field_t { - constexpr auto operator&&(auto) const -> bool { return false; } -}; - -template -constexpr auto field = field_t{}; -} // namespace safe diff --git a/test/safe/array.cpp b/test/safe/array.cpp index 308f5aa..cc682f4 100644 --- a/test/safe/array.cpp +++ b/test/safe/array.cpp @@ -16,27 +16,27 @@ TEST(safe_array_test, construction) { safe::array t{}; } TEST(safe_array_test, bracket_op) { safe::array t{}; - t[12_s32] = 42; + t[12_cn] = 42; - EXPECT_EQ(t[12_s32], 42); + EXPECT_EQ(t[12_cn], 42); } TEST(safe_array_test, const_bracket_op) { safe::array const t{0, 13, 2}; - EXPECT_EQ(t[1_s32], 13); + EXPECT_EQ(t[1_cn], 13); } TEST(safe_array_test, at) { safe::array t{}; - t[12_s32] = 42; + t[12_cn] = 42; - EXPECT_EQ(t.at(12_s32), 42); + EXPECT_EQ(t.at(12_cn), 42); } TEST(safe_array_test, const_at) { safe::array const t{0, 13, 2}; - EXPECT_EQ(t.at(1_s32), 13); + EXPECT_EQ(t.at(1_cn), 13); } diff --git a/test/safe/array/array_constant_access_max_violation.cpp b/test/safe/array/array_constant_access_max_violation.cpp index 291d8df..597f68a 100644 --- a/test/safe/array/array_constant_access_max_violation.cpp +++ b/test/safe/array/array_constant_access_max_violation.cpp @@ -6,5 +6,5 @@ using namespace safe::literals; void test() { safe::array my_data{}; - int value_one = my_data[10_u64]; + int value_one = my_data[10_cn]; } diff --git a/test/safe/constrained_number.cpp b/test/safe/constrained_number.cpp index d1ceabc..a7db063 100644 --- a/test/safe/constrained_number.cpp +++ b/test/safe/constrained_number.cpp @@ -23,13 +23,13 @@ using safe_int_types = TYPED_TEST_SUITE(safe_int_assign_test, safe_int_types); TYPED_TEST(safe_int_assign_test, init_vars) { - TypeParam value{42_s32}; + TypeParam value{42_cn}; EXPECT_EQ(value.unsafe_value(), 42); } TYPED_TEST(safe_int_assign_test, assign_vars) { - TypeParam value{0_s32}; - value = 33_s32; + TypeParam value{0_cn}; + value = 33_cn; EXPECT_EQ(value.unsafe_value(), 33); } @@ -41,58 +41,58 @@ using std_int_types = ::testing::Types, TypeParam> const a = 13_s32; - safe::constrained_number, TypeParam> const b = 29_s32; + safe::constrained_number, TypeParam> const a = 13_cn; + safe::constrained_number, TypeParam> const b = 29_cn; auto const result = a + b; EXPECT_EQ(result.unsafe_value(), 42); } TYPED_TEST(safe_var_ops_test, minus_op) { - safe::constrained_number, TypeParam> const a = 32_s32; - safe::constrained_number, TypeParam> const b = 8_s32; + safe::constrained_number, TypeParam> const a = 32_cn; + safe::constrained_number, TypeParam> const b = 8_cn; auto const result = a - b; EXPECT_EQ(result.unsafe_value(), 24); } TYPED_TEST(safe_var_ops_test, multiply_op) { - safe::constrained_number, TypeParam> const a = 6_s32; - safe::constrained_number, TypeParam> const b = 8_s32; + safe::constrained_number, TypeParam> const a = 6_cn; + safe::constrained_number, TypeParam> const b = 8_cn; auto const result = a * b; EXPECT_EQ(result.unsafe_value(), 48); } TYPED_TEST(safe_var_ops_test, divide_op) { - safe::constrained_number, TypeParam> const a = 45_s32; - safe::constrained_number, TypeParam> const b = 5_s32; + safe::constrained_number, TypeParam> const a = 45_cn; + safe::constrained_number, TypeParam> const b = 5_cn; auto const result = a / b; EXPECT_EQ(result.unsafe_value(), 9); } // FIXME: MODULO NEEDS TO BE REWRITTEN // TYPED_TEST(safe_var_ops_test, modulo_op) { -// safe::constrained_number, TypeParam> const a = 12_s32; -// safe::constrained_number, TypeParam> const b = 5_s32; +// safe::constrained_number, TypeParam> const a = 12_cn; +// safe::constrained_number, TypeParam> const b = 5_cn; // auto const result = a % b; // EXPECT_EQ(result.unsafe_value(), 2); //} // TYPED_TEST(safe_var_ops_test, left_shift_op) { -// safe::constrained_number, TypeParam> const a = 8_s32; -// safe::constrained_number, TypeParam> const b = 2_s32; +// safe::constrained_number, TypeParam> const a = 8_cn; +// safe::constrained_number, TypeParam> const b = 2_cn; // auto const result = a << b; // EXPECT_EQ(result.unsafe_value(), 32); //} // TYPED_TEST(safe_var_ops_test, right_shift_op) { -// safe::constrained_number, TypeParam> const a = 48_s32; -// safe::constrained_number, TypeParam> const b = 2_s32; +// safe::constrained_number, TypeParam> const a = 48_cn; +// safe::constrained_number, TypeParam> const b = 2_cn; // auto const result = a >> b; // EXPECT_EQ(result.unsafe_value(), 12); //} TYPED_TEST(safe_var_ops_test, spaceship_op) { - safe::constrained_number, TypeParam> const a = 45_s32; - safe::constrained_number, TypeParam> const b = 87_s32; + safe::constrained_number, TypeParam> const a = 45_cn; + safe::constrained_number, TypeParam> const b = 87_cn; EXPECT_TRUE(a == a); EXPECT_TRUE(b == b); @@ -114,22 +114,22 @@ TYPED_TEST(safe_var_ops_test, spaceship_op) { } TYPED_TEST(safe_var_ops_test, max_op) { - safe::constrained_number, TypeParam> const a = 48_s32; - safe::constrained_number, TypeParam> const b = 32_s32; + safe::constrained_number, TypeParam> const a = 48_cn; + safe::constrained_number, TypeParam> const b = 32_cn; auto const result = std::max(a, b); EXPECT_EQ(result.unsafe_value(), 48); } TYPED_TEST(safe_var_ops_test, min_op) { - safe::constrained_number, TypeParam> const a = 48_s32; - safe::constrained_number, TypeParam> const b = 32_s32; + safe::constrained_number, TypeParam> const a = 48_cn; + safe::constrained_number, TypeParam> const b = 32_cn; auto const result = std::min(a, b); EXPECT_EQ(result.unsafe_value(), 32); } TYPED_TEST(safe_var_ops_test, clamp_op) { - safe::constrained_number, TypeParam> const min = 32_s32; - safe::constrained_number, TypeParam> const max = 48_s32; + safe::constrained_number, TypeParam> const min = 32_cn; + safe::constrained_number, TypeParam> const max = 48_cn; auto const result = clamp(65, min, max); EXPECT_EQ(result.unsafe_value(), 48); } @@ -167,24 +167,24 @@ TEST(safe_var_test, assign_int_const) { } TEST(safe_var_test, negate_op) { - auto const result = -42_s32; + auto const result = -42_cn; EXPECT_EQ(result.unsafe_value(), -42); } TEST(safe_var_test, abs_op) { - auto const result = abs(-42_s32); + auto const result = abs(-42_cn); EXPECT_EQ(result.unsafe_value(), 42); } TEST(safe_var_test, bitwise_or_op) { - safe::constrained_number, uint32_t> const a = 15_s32; - safe::constrained_number, uint32_t> const b = 9_s32; + safe::constrained_number, uint32_t> const a = 15_cn; + safe::constrained_number, uint32_t> const b = 9_cn; auto const result = a & b; EXPECT_EQ(result.unsafe_value(), 9); } TEST(safe_var_test, add_int_const) { - safe::constrained_number, uint32_t> const a = 15_s32; + safe::constrained_number, uint32_t> const a = 15_cn; auto const result = a + std::integral_constant{}; EXPECT_EQ(result.unsafe_value(), 25); } diff --git a/test/safe/constrained_number/assign_constant_max_violation.cpp b/test/safe/constrained_number/assign_constant_max_violation.cpp index 4f000eb..0c5c63e 100644 --- a/test/safe/constrained_number/assign_constant_max_violation.cpp +++ b/test/safe/constrained_number/assign_constant_max_violation.cpp @@ -3,6 +3,6 @@ using namespace safe::literals; void test() { - safe::ival_s32<0, 10> test = 0_s32; - test = 20_s32; + safe::ival_s32<0, 10> test = 0_cn; + test = 20_cn; } diff --git a/test/safe/constrained_number/assign_constant_min_violation.cpp b/test/safe/constrained_number/assign_constant_min_violation.cpp index 38ba5e3..2908e7e 100644 --- a/test/safe/constrained_number/assign_constant_min_violation.cpp +++ b/test/safe/constrained_number/assign_constant_min_violation.cpp @@ -3,6 +3,6 @@ using namespace safe::literals; void test() { - safe::ival_s32<0, 10> test = 0_s32; - test = -1_s32; + safe::ival_s32<0, 10> test = 0_cn; + test = -1_cn; } diff --git a/test/safe/constrained_number/assign_lhs_rhs_union.cpp b/test/safe/constrained_number/assign_lhs_rhs_union.cpp index c1ab693..dc62e65 100644 --- a/test/safe/constrained_number/assign_lhs_rhs_union.cpp +++ b/test/safe/constrained_number/assign_lhs_rhs_union.cpp @@ -4,7 +4,7 @@ using namespace safe::literals; using safe::constrain_interval; void test() { - safe::constrained_number || constrain_interval<20, 30>, int> a = 0_s32; - safe::constrained_number || constrain_interval<21, 29>, int> b = 11_s32; + safe::constrained_number || constrain_interval<20, 30>, int> a = 0_cn; + safe::constrained_number || constrain_interval<21, 29>, int> b = 11_cn; a = b; } diff --git a/test/safe/constrained_number/assign_lhs_union.cpp b/test/safe/constrained_number/assign_lhs_union.cpp index e3e5e0f..64c457d 100644 --- a/test/safe/constrained_number/assign_lhs_union.cpp +++ b/test/safe/constrained_number/assign_lhs_union.cpp @@ -4,7 +4,7 @@ using namespace safe::literals; using safe::constrain_interval; void test() { - safe::constrained_number || constrain_interval<20, 30>, int> a = 0_s32; - safe::constrained_number, int> b = 11_s32; + safe::constrained_number || constrain_interval<20, 30>, int> a = 0_cn; + safe::constrained_number, int> b = 11_cn; a = b; } diff --git a/test/safe/constrained_number/assign_rhs_union.cpp b/test/safe/constrained_number/assign_rhs_union.cpp index a54b576..9ce106a 100644 --- a/test/safe/constrained_number/assign_rhs_union.cpp +++ b/test/safe/constrained_number/assign_rhs_union.cpp @@ -4,7 +4,7 @@ using namespace safe::literals; using safe::constrain_interval; void test() { - safe::constrained_number, int> a = 1_s32; - safe::constrained_number || constrain_interval<21, 29>, int> b = 0_s32; + safe::constrained_number, int> a = 1_cn; + safe::constrained_number || constrain_interval<21, 29>, int> b = 0_cn; a = b; } diff --git a/test/safe/constrained_number/construct_constant_max_violation.cpp b/test/safe/constrained_number/construct_constant_max_violation.cpp index e75e34b..d81d661 100644 --- a/test/safe/constrained_number/construct_constant_max_violation.cpp +++ b/test/safe/constrained_number/construct_constant_max_violation.cpp @@ -2,4 +2,4 @@ using namespace safe::literals; -void test() { safe::ival_s32<0, 10> test = 11_s32; } +void test() { safe::ival_s32<0, 10> test = 11_cn; } diff --git a/test/safe/constrained_number/construct_constant_min_violation.cpp b/test/safe/constrained_number/construct_constant_min_violation.cpp index b5a43c2..1282f91 100644 --- a/test/safe/constrained_number/construct_constant_min_violation.cpp +++ b/test/safe/constrained_number/construct_constant_min_violation.cpp @@ -2,4 +2,4 @@ using namespace safe::literals; -void test() { safe::ival_s32<0, 10> test = -1_s32; } +void test() { safe::ival_s32<0, 10> test = -1_cn; } diff --git a/test/safe/constrained_number/interval_larger_than_type.cpp b/test/safe/constrained_number/interval_larger_than_type.cpp index 34a12bf..bf929cc 100644 --- a/test/safe/constrained_number/interval_larger_than_type.cpp +++ b/test/safe/constrained_number/interval_larger_than_type.cpp @@ -2,4 +2,4 @@ using namespace safe::literals; -void test() { safe::ival_s8<-1000, 1000> test = 0_s8; } +void test() { safe::ival_s8<-1000, 1000> test = 0_cn; } diff --git a/test/safe/match.cpp b/test/safe/match.cpp index f9d0564..a11dd5b 100644 --- a/test/safe/match.cpp +++ b/test/safe/match.cpp @@ -39,13 +39,13 @@ TEST_F(safe_match_test, simple_pass) { TEST_F(safe_match_test, pass_with_an_input_var) { EXPECT_CALL(my_mock_function, two_safe_vars(9, 3)).Times(1); - safe::match(two_safe_vars, []() {})(9, 3_s32); + safe::match(two_safe_vars, []() {})(9, 3_cn); } TEST_F(safe_match_test, pass_with_both_input_vars) { EXPECT_CALL(my_mock_function, two_safe_vars(0, 4)).Times(1); - safe::match(two_safe_vars, []() {})(0_s32, 4_s32); + safe::match(two_safe_vars, []() {})(0_cn, 4_cn); } TEST_F(safe_match_test, simple_fail) { @@ -58,7 +58,7 @@ TEST_F(safe_match_test, simple_fail) { TEST_F(safe_match_test, fail_with_an_input_var) { EXPECT_CALL(my_mock_function, two_safe_vars(_, _)).Times(0); bool fail = false; - safe::match(two_safe_vars, [&]() { fail = true; })(11, 9_s32); + safe::match(two_safe_vars, [&]() { fail = true; })(11, 9_cn); EXPECT_TRUE(fail); } @@ -66,21 +66,21 @@ TEST_F(safe_match_test, char_to_ord_example) { auto const char_to_ord = safe::match( [](ival_s32<'0', '9'> dec_digit) { return dec_digit - s32_<'0'>; }, [](ival_s32<'a', 'f'> lower_hex_digit) { - return lower_hex_digit - s32_<'a'> + 10_s32; + return lower_hex_digit - s32_<'a'> + 10_cn; }, [](ival_s32<'A', 'F'> upper_hex_digit) { - return upper_hex_digit - s32_<'A'> + 10_s32; + return upper_hex_digit - s32_<'A'> + 10_cn; }, - []() { return 0_s32; }); - - EXPECT_EQ(char_to_ord('0'), 0_s32); - EXPECT_EQ(char_to_ord('3'), 3_s32); - EXPECT_EQ(char_to_ord('9'), 9_s32); - EXPECT_EQ(char_to_ord('a'), 10_s32); - EXPECT_EQ(char_to_ord('A'), 10_s32); - EXPECT_EQ(char_to_ord('f'), 15_s32); - EXPECT_EQ(char_to_ord('F'), 15_s32); - EXPECT_EQ(char_to_ord('g'), 0_s32); + []() { return 0_cn; }); + + EXPECT_EQ(char_to_ord('0'), 0_cn); + EXPECT_EQ(char_to_ord('3'), 3_cn); + EXPECT_EQ(char_to_ord('9'), 9_cn); + EXPECT_EQ(char_to_ord('a'), 10_cn); + EXPECT_EQ(char_to_ord('A'), 10_cn); + EXPECT_EQ(char_to_ord('f'), 15_cn); + EXPECT_EQ(char_to_ord('F'), 15_cn); + EXPECT_EQ(char_to_ord('g'), 0_cn); } TEST(safe_runtime_check_test, single_interval) { @@ -94,13 +94,13 @@ TEST(safe_runtime_check_test, single_interval) { } TEST(safe_runtime_check_test, single_interval_var) { - EXPECT_TRUE((safe::detail::check>(10_s32))); - EXPECT_TRUE((safe::detail::check>(0_s32))); - EXPECT_TRUE((safe::detail::check>(100_s32))); + EXPECT_TRUE((safe::detail::check>(10_cn))); + EXPECT_TRUE((safe::detail::check>(0_cn))); + EXPECT_TRUE((safe::detail::check>(100_cn))); - EXPECT_FALSE((safe::detail::check>(-10_s32))); - EXPECT_FALSE((safe::detail::check>(-1_s32))); - EXPECT_FALSE((safe::detail::check>(101_s32))); + EXPECT_FALSE((safe::detail::check>(-10_cn))); + EXPECT_FALSE((safe::detail::check>(-1_cn))); + EXPECT_FALSE((safe::detail::check>(101_cn))); } TEST(safe_runtime_check_test, interval_union) { From 02159910db1f008d343f94277b3aeadfdec498e6 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 09:13:40 +0900 Subject: [PATCH 06/13] fixing constrained_number constructors, move constraint_cast into own file --- include/safe/constrained_number.hpp | 135 +++++++++++++--------------- include/safe/constraint_cast.hpp | 30 +++++++ include/safe/detail/fwd.hpp | 32 ++----- include/safe/match.hpp | 3 +- 4 files changed, 100 insertions(+), 100 deletions(-) create mode 100644 include/safe/constraint_cast.hpp diff --git a/include/safe/constrained_number.hpp b/include/safe/constrained_number.hpp index f894e43..c002cbd 100644 --- a/include/safe/constrained_number.hpp +++ b/include/safe/constrained_number.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -17,19 +18,19 @@ #include namespace safe { -template struct constrained_number { +template struct constrained_number { public: - constexpr static auto constraint = Constraint; + constexpr static auto constraint = C; using value_type = T; constexpr constrained_number() - requires(constraint >= constrain_interval<0, 0>) + requires(constraint >= constrain_set<0>) : _raw_value(0) {} template requires(std::is_convertible_v) // NOLINTNEXTLINE(google-explicit-constructor) - SAFE_INLINE constexpr constrained_number(constraint_cast_ferry ferry) + SAFE_INLINE constexpr constrained_number(detail::constraint_cast_ferry ferry) : _raw_value{static_cast(ferry.value())} { SAFE_ASSUME(constraint.check(_raw_value)); } @@ -67,55 +68,45 @@ template struct constrained_number { return *this; } - // template - // requires (constraint <= dsl::constraint_of) - // [[nodiscard]] SAFE_INLINE constexpr operator U() { - // SAFE_ASSUME(constraint.check(_raw_value)); - // return _raw_value; - // } + template + requires (constraint <= dsl::constraint_of) + [[nodiscard]] SAFE_INLINE constexpr operator U() { + SAFE_ASSUME(constraint.check(_raw_value)); + return _raw_value; + } [[nodiscard]] SAFE_INLINE constexpr auto unsafe_value() const -> T { SAFE_ASSUME(constraint.check(_raw_value)); return _raw_value; } - using primitive_contract = detail::integral_type; - - static_assert(rhs_must_be_subset_of_lhs< - lhs_req, - rhs_req>::value); + static_assert(constraint <= dsl::constraint_of); /// @brief Do not use. Public to support use as non-type template parameter. T _raw_value; }; -template constexpr bool at_least_one_var = (... or any_constrained); +template +constrained_number(T) -> constrained_number, T>; -[[nodiscard]] SAFE_INLINE constexpr auto to_var(any_constrained auto v) -> any_constrained auto { - return v; -} +template +constrained_number(std::integral_constant) -> constrained_number, T>; + + +template constexpr bool at_least_one_cnum = (... or any_constrained); -template -[[nodiscard]] SAFE_INLINE constexpr auto to_var(std::integral_constant) - -> any_constrained auto { - return detail::make_constant(); -} -[[nodiscard]] SAFE_INLINE constexpr auto to_var(std::integral auto v) -> any_constrained - auto { - return value(v); -} template -concept var_admissable = requires(T t) { - { to_var(t) } -> any_constrained; +concept cnum_admissable = requires(T t) { + { constrained_number(t) } -> any_constrained; }; namespace detail { [[nodiscard]] SAFE_INLINE constexpr auto -bin_op(auto op, var_admissable auto raw_lhs, var_admissable auto raw_rhs) { - any_constrained auto const lhs = to_var(raw_lhs); - any_constrained auto const rhs = to_var(raw_rhs); +bin_op(auto op, cnum_admissable auto raw_lhs, cnum_admissable auto raw_rhs) { + auto const lhs = constrained_number(raw_lhs); + auto const rhs = constrained_number(raw_rhs); auto result_req = dsl::detail::simp(op(lhs.constraint, rhs.constraint)); @@ -128,14 +119,14 @@ bin_op(auto op, var_admissable auto raw_lhs, var_admissable auto raw_rhs) { } } // namespace detail -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator+(L lhs, R rhs) { return detail::bin_op(std::plus<>(), lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator-(L lhs, R rhs) { return detail::bin_op(std::minus<>(), lhs, rhs); } @@ -145,68 +136,68 @@ template return zero - v; } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator*(L lhs, R rhs) { return detail::bin_op(std::multiplies<>(), lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator/(L lhs, R rhs) { return detail::bin_op(std::divides<>(), lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator%(L lhs, R rhs) { return detail::bin_op(std::modulus<>(), lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator<<(L lhs, R rhs) { return detail::bin_op([](auto a, auto b) { return a << b; }, lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator>>(L lhs, R rhs) { return detail::bin_op([](auto a, auto b) { return a >> b; }, lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator|(L lhs, R rhs) { return detail::bin_op(std::bit_or<>(), lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator&(L lhs, R rhs) { return detail::bin_op(std::bit_and<>(), lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator^(L lhs, R rhs) { return detail::bin_op(std::bit_xor<>(), lhs, rhs); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator<=>(L raw_lhs, R raw_rhs) -> std::compare_three_way_result_t< - decltype(to_var(raw_lhs).unsafe_value()), - decltype(to_var(raw_rhs).unsafe_value())> { - return to_var(raw_lhs).unsafe_value() <=> to_var(raw_rhs).unsafe_value(); + decltype(constrained_number(raw_lhs).unsafe_value()), + decltype(constrained_number(raw_rhs).unsafe_value())> { + return constrained_number(raw_lhs).unsafe_value() <=> constrained_number(raw_rhs).unsafe_value(); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator==(L raw_lhs, R raw_rhs) -> bool { - return to_var(raw_lhs).unsafe_value() == to_var(raw_rhs).unsafe_value(); + return constrained_number(raw_lhs).unsafe_value() == constrained_number(raw_rhs).unsafe_value(); } [[nodiscard]] SAFE_INLINE constexpr auto abs(any_constrained auto value) { @@ -224,11 +215,11 @@ template return constraint_cast>(result); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto min(L raw_lhs, R raw_rhs) -> any_constrained auto { - any_constrained auto const lhs = to_var(raw_lhs); - any_constrained auto const rhs = to_var(raw_rhs); + auto const lhs = constrained_number(raw_lhs); + auto const rhs = constrained_number(raw_rhs); using common_type = std::common_type_t; @@ -239,11 +230,11 @@ template return constraint_cast>(result); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto max(L raw_lhs, R raw_rhs) -> any_constrained auto { - any_constrained auto const lhs = to_var(raw_lhs); - any_constrained auto const rhs = to_var(raw_rhs); + auto const lhs = constrained_number(raw_lhs); + auto const rhs = constrained_number(raw_rhs); using common_type = std::common_type_t; @@ -254,10 +245,10 @@ template return constraint_cast>(result); } -template - requires(at_least_one_var) +template + requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto clamp(T value, MinT min_val, MaxT max_val) -> any_constrained auto { - return min(max(to_var(value), to_var(min_val)), to_var(max_val)); + return min(max(constrained_number(value), constrained_number(min_val)), constrained_number(max_val)); } } // namespace safe diff --git a/include/safe/constraint_cast.hpp b/include/safe/constraint_cast.hpp new file mode 100644 index 0000000..33dca8d --- /dev/null +++ b/include/safe/constraint_cast.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace safe { +namespace detail { +template struct constraint_cast_ferry { + private: + T v; + + public: + SAFE_INLINE constexpr explicit(true) constraint_cast_ferry(T new_value) + : v{new_value} {} + + [[nodiscard]] SAFE_INLINE constexpr auto value() const -> T { return v; } +}; +} // namespace detail + +template + requires(any_constrained) +[[nodiscard]] constexpr auto constraint_cast(auto const &src) { + return T{detail::constraint_cast_ferry{src}}; +} + +template + requires(!any_constrained) +[[nodiscard]] constexpr auto constraint_cast(auto const &src) { + return src; +} +} // namespace safe \ No newline at end of file diff --git a/include/safe/detail/fwd.hpp b/include/safe/detail/fwd.hpp index dd162bd..f90f0e0 100644 --- a/include/safe/detail/fwd.hpp +++ b/include/safe/detail/fwd.hpp @@ -5,15 +5,15 @@ #endif namespace safe { -template struct constrained_number; +template struct constrained_number; -template constexpr bool is_var_v = false; +template constexpr bool is_constrained_number_v = false; -template -constexpr bool is_var_v> = true; +template +constexpr bool is_constrained_number_v> = true; template -concept any_constrained = is_var_v; +concept any_constrained = is_constrained_number_v; [[nodiscard]] constexpr inline auto value(auto value); @@ -22,27 +22,5 @@ template [[nodiscard]] constexpr inline auto make_constant(); } -template struct constraint_cast_ferry { - private: - T v; - - public: - SAFE_INLINE constexpr explicit(true) constraint_cast_ferry(T new_value) - : v{new_value} {} - - [[nodiscard]] SAFE_INLINE constexpr auto value() const -> T { return v; } -}; } // namespace safe - -template - requires(safe::any_constrained) -[[nodiscard]] constexpr auto constraint_cast(auto const &src) { - return T{safe::constraint_cast_ferry{src}}; -} - -template - requires(!safe::any_constrained) -[[nodiscard]] constexpr auto constraint_cast(auto const &src) { - return src; -} diff --git a/include/safe/match.hpp b/include/safe/match.hpp index b3d9127..da35f33 100644 --- a/include/safe/match.hpp +++ b/include/safe/match.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -71,7 +72,7 @@ template func_arg_types{}, std::forward(args)...); if (args_satisfy_reqs) { - return func(constraint_cast_ferry{ + return func(detail::constraint_cast_ferry{ detail::unwrap_var(std::forward(args))}...); } // check the remaining functions' requirements From ee00a6a74d7c81dbc8fd4d4af89910c77c97a758 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 09:30:01 +0900 Subject: [PATCH 07/13] rename Operand to `any_constraint` and use in constrained_number --- include/safe/constrained_number.hpp | 2 +- include/safe/detail/fwd.hpp | 8 ++++++-- include/safe/dsl/abs.hpp | 2 +- include/safe/dsl/add.hpp | 2 +- include/safe/dsl/bit_width.hpp | 2 +- include/safe/dsl/bitwise_and.hpp | 2 +- include/safe/dsl/bitwise_invert.hpp | 2 +- include/safe/dsl/bitwise_or.hpp | 2 +- include/safe/dsl/bitwise_xor.hpp | 2 +- include/safe/dsl/divide.hpp | 2 +- include/safe/dsl/fwd.hpp | 12 ++++++------ include/safe/dsl/intersection.hpp | 2 +- include/safe/dsl/is_equal.hpp | 4 ++-- include/safe/dsl/is_subset.hpp | 2 +- include/safe/dsl/is_superset.hpp | 2 +- include/safe/dsl/max.hpp | 2 +- include/safe/dsl/min.hpp | 2 +- include/safe/dsl/minus.hpp | 2 +- include/safe/dsl/modulo.hpp | 2 +- include/safe/dsl/multiply.hpp | 2 +- include/safe/dsl/shift_left.hpp | 2 +- include/safe/dsl/shift_right.hpp | 2 +- include/safe/dsl/union.hpp | 2 +- 23 files changed, 34 insertions(+), 30 deletions(-) diff --git a/include/safe/constrained_number.hpp b/include/safe/constrained_number.hpp index c002cbd..57e3ba1 100644 --- a/include/safe/constrained_number.hpp +++ b/include/safe/constrained_number.hpp @@ -18,7 +18,7 @@ #include namespace safe { -template struct constrained_number { +template struct constrained_number { public: constexpr static auto constraint = C; using value_type = T; diff --git a/include/safe/detail/fwd.hpp b/include/safe/detail/fwd.hpp index f90f0e0..1090174 100644 --- a/include/safe/detail/fwd.hpp +++ b/include/safe/detail/fwd.hpp @@ -1,15 +1,19 @@ #pragma once + +#include + + #ifndef SAFE_INLINE #define SAFE_INLINE inline #endif namespace safe { -template struct constrained_number; +template struct constrained_number; template constexpr bool is_constrained_number_v = false; -template +template constexpr bool is_constrained_number_v> = true; template diff --git a/include/safe/dsl/abs.hpp b/include/safe/dsl/abs.hpp index cdd4881..147d049 100644 --- a/include/safe/dsl/abs.hpp +++ b/include/safe/dsl/abs.hpp @@ -30,7 +30,7 @@ template struct abs_t : public detail::unary_op { std::max(detail::abs(val::min), detail::abs(val::max))>; }; -template [[nodiscard]] constexpr auto abs(T) -> abs_t { +template [[nodiscard]] constexpr auto abs(T) -> abs_t { return {}; } } // namespace safe::dsl diff --git a/include/safe/dsl/add.hpp b/include/safe/dsl/add.hpp index 30426b4..2ef534b 100644 --- a/include/safe/dsl/add.hpp +++ b/include/safe/dsl/add.hpp @@ -19,7 +19,7 @@ struct add : public detail::binary_op { using type = constrain_mask_t; }; -template +template [[nodiscard]] constexpr auto operator+(LhsT, RhsT) -> add { return {}; } diff --git a/include/safe/dsl/bit_width.hpp b/include/safe/dsl/bit_width.hpp index b4c1923..4145179 100644 --- a/include/safe/dsl/bit_width.hpp +++ b/include/safe/dsl/bit_width.hpp @@ -18,7 +18,7 @@ template struct bit_width_t : public detail::unary_op { std::bit_width(val::var_bits | val::const_bits)>; }; -template +template [[nodiscard]] constexpr auto bit_width(T) -> bit_width_t { return {}; } diff --git a/include/safe/dsl/bitwise_and.hpp b/include/safe/dsl/bitwise_and.hpp index e341a70..9c9af91 100644 --- a/include/safe/dsl/bitwise_and.hpp +++ b/include/safe/dsl/bitwise_and.hpp @@ -17,7 +17,7 @@ struct bitwise_and : public detail::binary_op { using type = constrain_mask_t; }; -template +template [[nodiscard]] constexpr auto operator&(LhsT, RhsT) -> bitwise_and { return {}; } diff --git a/include/safe/dsl/bitwise_invert.hpp b/include/safe/dsl/bitwise_invert.hpp index ac40462..29211d9 100644 --- a/include/safe/dsl/bitwise_invert.hpp +++ b/include/safe/dsl/bitwise_invert.hpp @@ -15,7 +15,7 @@ struct bitwise_invert : public detail::unary_op { using type = constrain_mask_t; }; -template +template [[nodiscard]] constexpr auto operator~(T) -> bitwise_invert { return {}; } diff --git a/include/safe/dsl/bitwise_or.hpp b/include/safe/dsl/bitwise_or.hpp index 6b3c450..9da9cc6 100644 --- a/include/safe/dsl/bitwise_or.hpp +++ b/include/safe/dsl/bitwise_or.hpp @@ -9,7 +9,7 @@ namespace safe::dsl { template struct bitwise_or : public detail::binary_op {}; -template +template [[nodiscard]] constexpr auto operator|(LhsT, RhsT) -> bitwise_or { return {}; } diff --git a/include/safe/dsl/bitwise_xor.hpp b/include/safe/dsl/bitwise_xor.hpp index 713778a..7d44c9f 100644 --- a/include/safe/dsl/bitwise_xor.hpp +++ b/include/safe/dsl/bitwise_xor.hpp @@ -17,7 +17,7 @@ struct bitwise_xor : public detail::binary_op { using type = constrain_mask_t; }; -template +template [[nodiscard]] constexpr auto operator^(LhsT, RhsT) -> bitwise_xor { return {}; } diff --git a/include/safe/dsl/divide.hpp b/include/safe/dsl/divide.hpp index 0b966b6..0926eb7 100644 --- a/include/safe/dsl/divide.hpp +++ b/include/safe/dsl/divide.hpp @@ -20,7 +20,7 @@ struct divide, constrain_interval_t>; }; -template +template [[nodiscard]] constexpr auto operator/(LhsT, RhsT) -> divide { return {}; } diff --git a/include/safe/dsl/fwd.hpp b/include/safe/dsl/fwd.hpp index 4bbcd48..fec84f8 100644 --- a/include/safe/dsl/fwd.hpp +++ b/include/safe/dsl/fwd.hpp @@ -34,11 +34,11 @@ template constexpr auto max = [] { return max2, max2>; }(); } // namespace safe::dsl::detail -namespace safe::dsl { +namespace safe { template -concept Operand = std::is_base_of_v || - std::is_base_of_v || - std::is_base_of_v || - std::is_base_of_v; +concept any_constraint = std::is_base_of_v || + std::is_base_of_v || + std::is_base_of_v || + std::is_base_of_v; -} +} // namespace safe diff --git a/include/safe/dsl/intersection.hpp b/include/safe/dsl/intersection.hpp index 24b6ce9..bc3d934 100644 --- a/include/safe/dsl/intersection.hpp +++ b/include/safe/dsl/intersection.hpp @@ -7,7 +7,7 @@ template struct intersection_t : public detail::set_op { using type = intersection_t; }; -template +template [[nodiscard]] constexpr auto operator&&(LhsT, RhsT) -> intersection_t { return {}; diff --git a/include/safe/dsl/is_equal.hpp b/include/safe/dsl/is_equal.hpp index d84117a..15471db 100644 --- a/include/safe/dsl/is_equal.hpp +++ b/include/safe/dsl/is_equal.hpp @@ -6,7 +6,7 @@ #include namespace safe::dsl { -template +template [[nodiscard]] constexpr auto operator==(LhsT lhs, RhsT rhs) -> bool { auto const simp_lhs = detail::simp(lhs); auto const simp_rhs = detail::simp(rhs); @@ -14,7 +14,7 @@ template return (simp_lhs <= simp_rhs) && (simp_lhs >= simp_rhs); } -template +template [[nodiscard]] constexpr auto operator!=(LhsT lhs, RhsT rhs) -> bool { return !(lhs == rhs); } diff --git a/include/safe/dsl/is_subset.hpp b/include/safe/dsl/is_subset.hpp index 2bccfb1..5c9181a 100644 --- a/include/safe/dsl/is_subset.hpp +++ b/include/safe/dsl/is_subset.hpp @@ -66,7 +66,7 @@ struct is_subset, [[nodiscard]] constexpr explicit operator bool() const { return value; } }; -template +template [[nodiscard]] constexpr auto operator<=(LhsT, RhsT) -> bool { return static_cast(detail::eval_v>); } diff --git a/include/safe/dsl/is_superset.hpp b/include/safe/dsl/is_superset.hpp index 475b502..d4155fa 100644 --- a/include/safe/dsl/is_superset.hpp +++ b/include/safe/dsl/is_superset.hpp @@ -7,7 +7,7 @@ namespace safe::dsl { template using is_superset = is_subset; -template +template [[nodiscard]] constexpr auto operator>=(LhsT, RhsT) -> bool { return static_cast(detail::eval_v>); } diff --git a/include/safe/dsl/max.hpp b/include/safe/dsl/max.hpp index b42450d..678e8ab 100644 --- a/include/safe/dsl/max.hpp +++ b/include/safe/dsl/max.hpp @@ -18,7 +18,7 @@ struct max_t, constrain_interval_t; }; -template +template [[nodiscard]] constexpr auto max(LhsT, RhsT) -> max_t { return {}; } diff --git a/include/safe/dsl/min.hpp b/include/safe/dsl/min.hpp index 381fa3b..18f2436 100644 --- a/include/safe/dsl/min.hpp +++ b/include/safe/dsl/min.hpp @@ -18,7 +18,7 @@ struct min_t, constrain_interval_t; }; -template +template [[nodiscard]] constexpr auto min(LhsT, RhsT) -> min_t { return {}; } diff --git a/include/safe/dsl/minus.hpp b/include/safe/dsl/minus.hpp index 8c33b20..78a5fb3 100644 --- a/include/safe/dsl/minus.hpp +++ b/include/safe/dsl/minus.hpp @@ -18,7 +18,7 @@ struct minus : public detail::binary_op { using type = constrain_mask_t; }; -template +template [[nodiscard]] constexpr auto operator-(LhsT, RhsT) -> minus { return {}; } diff --git a/include/safe/dsl/modulo.hpp b/include/safe/dsl/modulo.hpp index 585438f..9e74394 100644 --- a/include/safe/dsl/modulo.hpp +++ b/include/safe/dsl/modulo.hpp @@ -86,7 +86,7 @@ struct modulo, constrain_interval_t; }; -template +template [[nodiscard]] constexpr auto operator%(LhsT, RhsT) -> modulo { return {}; } diff --git a/include/safe/dsl/multiply.hpp b/include/safe/dsl/multiply.hpp index f437330..c5ca856 100644 --- a/include/safe/dsl/multiply.hpp +++ b/include/safe/dsl/multiply.hpp @@ -18,7 +18,7 @@ struct multiply, constrain_interval_t>; }; -template +template [[nodiscard]] constexpr auto operator*(LhsT, RhsT) -> multiply { return {}; } diff --git a/include/safe/dsl/shift_left.hpp b/include/safe/dsl/shift_left.hpp index 6b9d74f..5661d97 100644 --- a/include/safe/dsl/shift_left.hpp +++ b/include/safe/dsl/shift_left.hpp @@ -34,7 +34,7 @@ struct shift_left, shift_left>>; }; -template +template [[nodiscard]] constexpr auto operator<<(LhsT, RhsT) -> shift_left { return {}; } diff --git a/include/safe/dsl/shift_right.hpp b/include/safe/dsl/shift_right.hpp index 73bdb0b..8258955 100644 --- a/include/safe/dsl/shift_right.hpp +++ b/include/safe/dsl/shift_right.hpp @@ -38,7 +38,7 @@ struct shift_right, shift_right>>; }; -template +template [[nodiscard]] constexpr auto operator>>(LhsT, RhsT) -> shift_right { return {}; } diff --git a/include/safe/dsl/union.hpp b/include/safe/dsl/union.hpp index 9d2a58a..01b1d3f 100644 --- a/include/safe/dsl/union.hpp +++ b/include/safe/dsl/union.hpp @@ -13,7 +13,7 @@ template struct union_t : public detail::set_op { } }; -template +template [[nodiscard]] constexpr auto operator||(LhsT, RhsT) -> union_t { return {}; } From 6b4ed20a5d849b09fd1d421c8082c8316ce7a745 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 09:44:13 +0900 Subject: [PATCH 08/13] fix constraint_cast to match P2993R0 --- docs/overview.adoc | 14 +++++++------- include/safe/algorithm/accumulate.hpp | 2 +- include/safe/algorithm/irange.hpp | 2 +- include/safe/constant.hpp | 5 ++--- include/safe/constrained_number.hpp | 10 +++++----- include/safe/constraint_cast.hpp | 14 +++++--------- include/safe/detail/fwd.hpp | 4 ---- include/safe/detail/make_constant.hpp | 11 ----------- include/safe/int.hpp | 2 +- include/safe/value.hpp | 2 +- test/safe/dsl.cpp | 4 ++-- 11 files changed, 25 insertions(+), 45 deletions(-) delete mode 100644 include/safe/detail/make_constant.hpp diff --git a/docs/overview.adoc b/docs/overview.adoc index a816d60..b6c1a42 100644 --- a/docs/overview.adoc +++ b/docs/overview.adoc @@ -216,18 +216,18 @@ void event_count_interrupt_handler() { reference section. The final method of introducing values into the safe arithmetic environment is -through `constraint_cast(value)`. It bypasses all compile-time and runtime +through `constraint_cast(value)`. It bypasses all compile-time and runtime safety checks and depends on the value to be proven to satisfy the requirements of `T` using mechanisms outside the visibility and scope of the Safe Arithmetic library. Its use is highly discouraged. The name is chosen to cause an uneasy feeling in programmers and clearly signal a red flag for code reviewers. ```c++ -template -T constraint_cast(auto value); +template +constrained_number constraint_cast(T value); ``` -`constraint_cast(value)` is used within the Safe Arithmetic library to ferry +`constraint_cast(value)` is used within the Safe Arithmetic library to ferry values into instances of `safe::constrained_number` after proving it is safe to do so. It is necessary for the library's construction. @@ -235,10 +235,10 @@ As always, an example is useful to illustrate how to use a particular function. ```c++ std::uint16_t some_function(); -void do_something_useful(safe::ival_u32<0, 1024> useful_value); +void do_something_useful(safe::constrain_interval<0, 1024> useful_value); // VERY DANGEROUS: Don't do this! -auto dangerous_value = constraint_cast>(some_function()); +auto dangerous_value = constraint_cast>(some_function()); do_something_useful(dangerous_value); // SAFE: Use safe::match instead. This will only call 'do_something_useful' @@ -246,7 +246,7 @@ do_something_useful(dangerous_value); // 'useful_value'. If it doesn't match, the default callable will be invoked. safe::match(do_something_useful, [](){})(some_function()); -// SAFE: Don't use constraint_cast(value), try almost everything else first. +// SAFE: Don't use constraint_cast(value), try almost everything else first. ``` If you find a case where you feel you must use `constraint_cast`, then maybe there diff --git a/include/safe/algorithm/accumulate.hpp b/include/safe/algorithm/accumulate.hpp index 2123472..e87c666 100644 --- a/include/safe/algorithm/accumulate.hpp +++ b/include/safe/algorithm/accumulate.hpp @@ -72,7 +72,7 @@ template iter_count++; } - return constraint_cast>(sum); + return constraint_cast(sum); } template diff --git a/include/safe/algorithm/irange.hpp b/include/safe/algorithm/irange.hpp index b8e8d3e..c942f6d 100644 --- a/include/safe/algorithm/irange.hpp +++ b/include/safe/algorithm/irange.hpp @@ -22,7 +22,7 @@ template struct irange { : parent_{parent}, value_{value}, end_{end} {} constexpr auto operator*() const -> ret_t { - return constraint_cast(value_); + return reinterpret_cast(value_); } constexpr auto operator++() { diff --git a/include/safe/constant.hpp b/include/safe/constant.hpp index a2ccd01..4b931df 100644 --- a/include/safe/constant.hpp +++ b/include/safe/constant.hpp @@ -1,11 +1,10 @@ #pragma once -#include #include #include namespace safe { template -constexpr constrained_number, U> constant = - detail::make_constant(); +constexpr auto constant = + constrained_number(std::integral_constant{}); } \ No newline at end of file diff --git a/include/safe/constrained_number.hpp b/include/safe/constrained_number.hpp index 57e3ba1..d384879 100644 --- a/include/safe/constrained_number.hpp +++ b/include/safe/constrained_number.hpp @@ -115,7 +115,7 @@ bin_op(auto op, cnum_admissable auto raw_lhs, cnum_admissable auto raw_rhs) { auto result = op(lhs.unsafe_value(), rhs.unsafe_value()); - return constraint_cast>(result); + return constraint_cast(result); } } // namespace detail @@ -204,7 +204,7 @@ template using num_t = decltype(value.unsafe_value()); auto result = static_cast(std::abs(value.unsafe_value())); auto result_req = dsl::detail::simp(safe::dsl::abs(value.constraint)); - return constraint_cast>(result); + return constraint_cast(result); } [[nodiscard]] SAFE_INLINE constexpr auto bit_width(any_constrained auto value) { @@ -212,7 +212,7 @@ template auto result = static_cast(std::bit_width(value.unsafe_value())); auto result_req = dsl::detail::simp(safe::dsl::bit_width(value.constraint)); - return constraint_cast>(result); + return constraint_cast(result); } template @@ -227,7 +227,7 @@ template using result_t = decltype(result); auto result_req = dsl::detail::simp(dsl::min(lhs.constraint, rhs.constraint)); - return constraint_cast>(result); + return constraint_cast(result); } template @@ -242,7 +242,7 @@ template using result_t = decltype(result); auto result_req = dsl::detail::simp(dsl::max(lhs.constraint, rhs.constraint)); - return constraint_cast>(result); + return constraint_cast(result); } template diff --git a/include/safe/constraint_cast.hpp b/include/safe/constraint_cast.hpp index 33dca8d..f79f035 100644 --- a/include/safe/constraint_cast.hpp +++ b/include/safe/constraint_cast.hpp @@ -2,6 +2,8 @@ #include +#include + namespace safe { namespace detail { template struct constraint_cast_ferry { @@ -16,15 +18,9 @@ template struct constraint_cast_ferry { }; } // namespace detail -template - requires(any_constrained) -[[nodiscard]] constexpr auto constraint_cast(auto const &src) { - return T{detail::constraint_cast_ferry{src}}; +template +[[nodiscard]] constexpr auto constraint_cast(T const & src) -> constrained_number { + return {detail::constraint_cast_ferry{src}}; } -template - requires(!any_constrained) -[[nodiscard]] constexpr auto constraint_cast(auto const &src) { - return src; -} } // namespace safe \ No newline at end of file diff --git a/include/safe/detail/fwd.hpp b/include/safe/detail/fwd.hpp index 1090174..9d9ba80 100644 --- a/include/safe/detail/fwd.hpp +++ b/include/safe/detail/fwd.hpp @@ -21,10 +21,6 @@ concept any_constrained = is_constrained_number_v; [[nodiscard]] constexpr inline auto value(auto value); -namespace detail { -template -[[nodiscard]] constexpr inline auto make_constant(); -} } // namespace safe diff --git a/include/safe/detail/make_constant.hpp b/include/safe/detail/make_constant.hpp deleted file mode 100644 index 2c8bb8d..0000000 --- a/include/safe/detail/make_constant.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -namespace safe::detail { -template -[[nodiscard]] constexpr inline auto make_constant() { - return constraint_cast, U>>(value); -} -} // namespace safe::detail \ No newline at end of file diff --git a/include/safe/int.hpp b/include/safe/int.hpp index e859b4f..8dd0ec8 100644 --- a/include/safe/int.hpp +++ b/include/safe/int.hpp @@ -74,7 +74,7 @@ template return sum; }(); - return make_constant(); + return constrained_number(std::integral_constant{}); } } // namespace detail diff --git a/include/safe/value.hpp b/include/safe/value.hpp index 1ecc7c7..bdd0323 100644 --- a/include/safe/value.hpp +++ b/include/safe/value.hpp @@ -10,6 +10,6 @@ constexpr auto value(auto value) { using value_t = decltype(value); constexpr value_t min = std::numeric_limits::lowest(); constexpr value_t max = std::numeric_limits::max(); - return constraint_cast, value_t>>(value); + return constraint_cast, value_t>(value); } } // namespace safe \ No newline at end of file diff --git a/test/safe/dsl.cpp b/test/safe/dsl.cpp index 95933cb..bdab317 100644 --- a/test/safe/dsl.cpp +++ b/test/safe/dsl.cpp @@ -21,8 +21,8 @@ template lhs, constrain_interval_t rhs, OperationT op) { auto const actual = safe::dsl::detail::simp(op(lhs, rhs)); - for (T i = lhs_min; i <= lhs_max; i = constraint_cast(i + 1)) { - for (T j = rhs_min; j <= rhs_max; j = constraint_cast(j + 1)) { + for (T i = lhs_min; i <= lhs_max; i = reinterpret_cast(i + 1)) { + for (T j = rhs_min; j <= rhs_max; j = reinterpret_cast(j + 1)) { auto const v = op(i, j); EXPECT_GE(v, actual.min); EXPECT_LE(v, actual.max); From 8c8c3a6e1c87fd8daefd7107083022653c5b1d1d Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 09:46:28 +0900 Subject: [PATCH 09/13] remove value.hpp and associated function --- include/safe.hpp | 1 - include/safe/detail/fwd.hpp | 2 -- include/safe/value.hpp | 15 --------------- 3 files changed, 18 deletions(-) delete mode 100644 include/safe/value.hpp diff --git a/include/safe.hpp b/include/safe.hpp index 0fee4a8..b3b9a89 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -6,5 +6,4 @@ #include #include #include -#include #include diff --git a/include/safe/detail/fwd.hpp b/include/safe/detail/fwd.hpp index 9d9ba80..01398b4 100644 --- a/include/safe/detail/fwd.hpp +++ b/include/safe/detail/fwd.hpp @@ -19,8 +19,6 @@ constexpr bool is_constrained_number_v> = true; template concept any_constrained = is_constrained_number_v; -[[nodiscard]] constexpr inline auto value(auto value); - } // namespace safe diff --git a/include/safe/value.hpp b/include/safe/value.hpp deleted file mode 100644 index bdd0323..0000000 --- a/include/safe/value.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace safe { -constexpr auto value(auto value) { - using value_t = decltype(value); - constexpr value_t min = std::numeric_limits::lowest(); - constexpr value_t max = std::numeric_limits::max(); - return constraint_cast, value_t>(value); -} -} // namespace safe \ No newline at end of file From c32f15984cc11bf0a208b1ef140c8c99524050e7 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 09:49:49 +0900 Subject: [PATCH 10/13] import constraint_of into safe namespace --- include/safe/constrained_number.hpp | 10 +++++----- include/safe/dsl.hpp | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/safe/constrained_number.hpp b/include/safe/constrained_number.hpp index d384879..1dd8b09 100644 --- a/include/safe/constrained_number.hpp +++ b/include/safe/constrained_number.hpp @@ -37,7 +37,7 @@ template struct constrained_number { template requires(std::integral && - (constraint >= dsl::constraint_of)) + (constraint >= constraint_of)) // NOLINTNEXTLINE(google-explicit-constructor) SAFE_INLINE constexpr constrained_number(U rhs) : _raw_value{rhs} {} @@ -69,7 +69,7 @@ template struct constrained_number { } template - requires (constraint <= dsl::constraint_of) + requires (constraint <= constraint_of) [[nodiscard]] SAFE_INLINE constexpr operator U() { SAFE_ASSUME(constraint.check(_raw_value)); return _raw_value; @@ -80,17 +80,17 @@ template struct constrained_number { return _raw_value; } - static_assert(constraint <= dsl::constraint_of); + static_assert(constraint <= constraint_of); /// @brief Do not use. Public to support use as non-type template parameter. T _raw_value; }; template -constrained_number(T) -> constrained_number, T>; +constrained_number(T) -> constrained_number, T>; template -constrained_number(std::integral_constant) -> constrained_number, T>; +constrained_number(std::integral_constant) -> constrained_number, T>; template constexpr bool at_least_one_cnum = (... or any_constrained); diff --git a/include/safe/dsl.hpp b/include/safe/dsl.hpp index 549f299..d9bf72f 100644 --- a/include/safe/dsl.hpp +++ b/include/safe/dsl.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -37,4 +38,6 @@ using safe::dsl::constrain_set_t; using safe::dsl::intersection_t; using safe::dsl::union_t; + +using safe::dsl::constraint_of; } // namespace safe \ No newline at end of file From fc7ed59c7443241d77e9f90c68ba4286ed054084 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 10:22:42 +0900 Subject: [PATCH 11/13] add make_constrained --- include/safe.hpp | 1 + include/safe/make_constrained.hpp | 32 +++++++++++++++++++++++++ test/safe/CMakeLists.txt | 1 + test/safe/make_constrained.cpp | 39 +++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 include/safe/make_constrained.hpp create mode 100644 test/safe/make_constrained.cpp diff --git a/include/safe.hpp b/include/safe.hpp index b3b9a89..a77b6f8 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -7,3 +7,4 @@ #include #include #include +#include diff --git a/include/safe/make_constrained.hpp b/include/safe/make_constrained.hpp new file mode 100644 index 0000000..00311f8 --- /dev/null +++ b/include/safe/make_constrained.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include + +namespace safe { + auto const constraint_violation = + std::range_error("make_constrained constraint violation error"); + + template + auto make_constrained(T value) -> constrained_number { + if (C.check(value)) { + return constraint_cast(value); + } else { + throw constraint_violation; + } + } + + template + requires (T::constraint <= C) + constexpr auto make_constrained(T value) -> T { + return value; + } + + template + requires (constrain_set <= C) + constexpr auto make_constrained(std::integral_constant x) -> T { + return constrained_number(x); + } +} \ No newline at end of file diff --git a/test/safe/CMakeLists.txt b/test/safe/CMakeLists.txt index ca3bfb6..0cad586 100644 --- a/test/safe/CMakeLists.txt +++ b/test/safe/CMakeLists.txt @@ -19,6 +19,7 @@ endfunction() add_test_suites( constrained_number.cpp + make_constrained.cpp match.cpp array.cpp dsl/add.cpp diff --git a/test/safe/make_constrained.cpp b/test/safe/make_constrained.cpp new file mode 100644 index 0000000..8a54a7e --- /dev/null +++ b/test/safe/make_constrained.cpp @@ -0,0 +1,39 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +#include + +using namespace safe::literals; +using namespace safe; + +TEST(make_constrained_test, make_from_cn_udl) { + auto from = 10_cn; + constexpr auto result = + make_constrained>(10_cn); + + using result_t = std::remove_cvref_t; + + EXPECT_EQ(result, from); + EXPECT_TRUE((std::is_same_v)); +} + +TEST(make_constrained_test, make_from_integral_constant) { + constexpr auto result = + make_constrained>(std::integral_constant{}); + + EXPECT_EQ(result, 10); +} + +TEST(make_constrained_test, make_with_runtime_check) { + auto result = + make_constrained>(10); + + EXPECT_EQ(result, 10); +} + +TEST(make_constrained_test, make_with_failing_runtime_check) { + EXPECT_THAT([]() { (make_constrained>(-10)); }, + ThrowsMessage(testing::HasSubstr("make_constrained constraint violation error"))); +} \ No newline at end of file From 99cc59e91cbe2f4fa460f5025356ecb968cf86fa Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 10:37:48 +0900 Subject: [PATCH 12/13] rename unsafe_value to raw_value --- README.md | 2 +- include/safe/algorithm/accumulate.hpp | 4 +-- include/safe/algorithm/irange.hpp | 10 +++--- include/safe/array.hpp | 8 ++--- include/safe/constrained_number.hpp | 40 +++++++++++----------- include/safe/detail/function.hpp | 4 +-- include/safe/iostream.hpp | 2 +- test/safe/constrained_number.cpp | 48 +++++++++++++-------------- test/safe/make_constrained.cpp | 2 +- test/safe/match.cpp | 2 +- 10 files changed, 61 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 06d0684..17274dc 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ parameter. constexpr T & operator[]( safe::constrained_number, std::size_t> index ) { - return storage[index.unsafe_value()]; + return storage[index.raw_value()]; } ``` diff --git a/include/safe/algorithm/accumulate.hpp b/include/safe/algorithm/accumulate.hpp index e87c666..d750da4 100644 --- a/include/safe/algorithm/accumulate.hpp +++ b/include/safe/algorithm/accumulate.hpp @@ -62,12 +62,12 @@ template constexpr auto req = decltype((*first).constraint){}; constexpr auto sum_req = detail::fold(req, op); - using ret_num_t = decltype((*first).unsafe_value()); + using ret_num_t = decltype((*first).raw_value()); auto iter_count = size_t{}; auto sum = init; while ((first != last) && (iter_count < max_iter)) { - sum = op(sum, (*first).unsafe_value()); + sum = op(sum, (*first).raw_value()); first++; iter_count++; } diff --git a/include/safe/algorithm/irange.hpp b/include/safe/algorithm/irange.hpp index c942f6d..bf4b727 100644 --- a/include/safe/algorithm/irange.hpp +++ b/include/safe/algorithm/irange.hpp @@ -29,7 +29,7 @@ template struct irange { auto const new_unsafe_value = value_++; // FIXME: consolidate range checks between == and ++ - if (new_unsafe_value < parent_->end_.unsafe_value()) { + if (new_unsafe_value < parent_->end_.raw_value()) { value_ = new_unsafe_value; } else { @@ -51,14 +51,14 @@ template struct irange { constexpr irange(BeginT begin, EndT end) : begin_{begin}, end_{end} {} constexpr auto begin() const { - return iterator{ - this, begin_.unsafe_value(), begin_ == end_}; + return iterator{ + this, begin_.raw_value(), begin_ == end_}; } constexpr auto end() const { // FIXME: need to find the right value for the end - return iterator{ - this, end_.unsafe_value() - 1, true}; + return iterator{ + this, end_.raw_value() - 1, true}; } }; } // namespace safe diff --git a/include/safe/array.hpp b/include/safe/array.hpp index c87e4bb..1be7e47 100644 --- a/include/safe/array.hpp +++ b/include/safe/array.hpp @@ -32,22 +32,22 @@ template struct array { [[nodiscard]] constexpr auto operator[](constrained_number, size_type> pos) -> reference { - return storage[pos.unsafe_value()]; + return storage[pos.raw_value()]; } [[nodiscard]] constexpr auto operator[](constrained_number, size_type> pos) const -> const_reference { - return storage[pos.unsafe_value()]; + return storage[pos.raw_value()]; } [[nodiscard]] constexpr auto at(constrained_number, size_type> pos) -> reference { - return storage[pos.unsafe_value()]; + return storage[pos.raw_value()]; } [[nodiscard]] constexpr auto at(constrained_number, size_type> pos) const -> const_reference { - return storage[pos.unsafe_value()]; + return storage[pos.raw_value()]; } [[nodiscard]] constexpr auto front() -> reference { diff --git a/include/safe/constrained_number.hpp b/include/safe/constrained_number.hpp index 1dd8b09..e918b2c 100644 --- a/include/safe/constrained_number.hpp +++ b/include/safe/constrained_number.hpp @@ -50,7 +50,7 @@ template struct constrained_number { // NOLINTNEXTLINE(google-explicit-constructor) SAFE_INLINE constexpr constrained_number(any_constrained auto const &rhs) - : _raw_value(rhs.unsafe_value()) // intentionally allowing narrowing + : _raw_value(rhs.raw_value()) // intentionally allowing narrowing // conversions { static_assert_assign_requirements(*this, rhs); @@ -58,13 +58,13 @@ template struct constrained_number { SAFE_INLINE constexpr auto operator=(any_constrained auto const &rhs) -> constrained_number & { static_assert_assign_requirements(*this, rhs); - _raw_value = rhs.unsafe_value(); + _raw_value = rhs.raw_value(); return *this; } SAFE_INLINE constexpr auto operator=(any_constrained auto &&rhs) -> constrained_number & { static_assert_assign_requirements(*this, rhs); - _raw_value = rhs.unsafe_value(); + _raw_value = rhs.raw_value(); return *this; } @@ -75,7 +75,7 @@ template struct constrained_number { return _raw_value; } - [[nodiscard]] SAFE_INLINE constexpr auto unsafe_value() const -> T { + [[nodiscard]] SAFE_INLINE constexpr auto raw_value() const -> T { SAFE_ASSUME(constraint.check(_raw_value)); return _raw_value; } @@ -113,7 +113,7 @@ bin_op(auto op, cnum_admissable auto raw_lhs, cnum_admissable auto raw_rhs) { // FIXME: replace with embiggening strategy; the result type is // always large enough to contain its value - auto result = op(lhs.unsafe_value(), rhs.unsafe_value()); + auto result = op(lhs.raw_value(), rhs.raw_value()); return constraint_cast(result); } @@ -132,7 +132,7 @@ template } [[nodiscard]] SAFE_INLINE constexpr auto operator-(any_constrained auto v) { - constexpr auto zero = constrained_number, decltype(v.unsafe_value())>{}; + constexpr auto zero = constrained_number, decltype(v.raw_value())>{}; return zero - v; } @@ -188,28 +188,28 @@ template requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator<=>(L raw_lhs, R raw_rhs) -> std::compare_three_way_result_t< - decltype(constrained_number(raw_lhs).unsafe_value()), - decltype(constrained_number(raw_rhs).unsafe_value())> { - return constrained_number(raw_lhs).unsafe_value() <=> constrained_number(raw_rhs).unsafe_value(); + decltype(constrained_number(raw_lhs).raw_value()), + decltype(constrained_number(raw_rhs).raw_value())> { + return constrained_number(raw_lhs).raw_value() <=> constrained_number(raw_rhs).raw_value(); } template requires(at_least_one_cnum) [[nodiscard]] SAFE_INLINE constexpr auto operator==(L raw_lhs, R raw_rhs) -> bool { - return constrained_number(raw_lhs).unsafe_value() == constrained_number(raw_rhs).unsafe_value(); + return constrained_number(raw_lhs).raw_value() == constrained_number(raw_rhs).raw_value(); } [[nodiscard]] SAFE_INLINE constexpr auto abs(any_constrained auto value) { - using num_t = decltype(value.unsafe_value()); - auto result = static_cast(std::abs(value.unsafe_value())); + using num_t = decltype(value.raw_value()); + auto result = static_cast(std::abs(value.raw_value())); auto result_req = dsl::detail::simp(safe::dsl::abs(value.constraint)); return constraint_cast(result); } [[nodiscard]] SAFE_INLINE constexpr auto bit_width(any_constrained auto value) { - using num_t = decltype(value.unsafe_value()); - auto result = static_cast(std::bit_width(value.unsafe_value())); + using num_t = decltype(value.raw_value()); + auto result = static_cast(std::bit_width(value.raw_value())); auto result_req = dsl::detail::simp(safe::dsl::bit_width(value.constraint)); return constraint_cast(result); @@ -221,9 +221,9 @@ template auto const lhs = constrained_number(raw_lhs); auto const rhs = constrained_number(raw_rhs); - using common_type = std::common_type_t; - auto result = std::min(lhs.unsafe_value(), rhs.unsafe_value()); + using common_type = std::common_type_t; + auto result = std::min(lhs.raw_value(), rhs.raw_value()); using result_t = decltype(result); auto result_req = dsl::detail::simp(dsl::min(lhs.constraint, rhs.constraint)); @@ -236,9 +236,9 @@ template auto const lhs = constrained_number(raw_lhs); auto const rhs = constrained_number(raw_rhs); - using common_type = std::common_type_t; - auto result = std::max(lhs.unsafe_value(), rhs.unsafe_value()); + using common_type = std::common_type_t; + auto result = std::max(lhs.raw_value(), rhs.raw_value()); using result_t = decltype(result); auto result_req = dsl::detail::simp(dsl::max(lhs.constraint, rhs.constraint)); diff --git a/include/safe/detail/function.hpp b/include/safe/detail/function.hpp index 0442b33..10b6286 100644 --- a/include/safe/detail/function.hpp +++ b/include/safe/detail/function.hpp @@ -22,7 +22,7 @@ template struct runtime= InputVarT::constraint) { return true; } else { - return VarT::constraint.check(input.unsafe_value()); + return VarT::constraint.check(input.raw_value()); }; } }; @@ -73,7 +73,7 @@ constexpr auto check(mp_list, ArgTs... args) -> bool { template [[nodiscard]] constexpr auto unwrap_var(T &&v) -> decltype(auto) { if constexpr (any_constrained>) { - return v.unsafe_value(); + return v.raw_value(); } else { return std::forward(v); } diff --git a/include/safe/iostream.hpp b/include/safe/iostream.hpp index 368e1da..839cfdd 100644 --- a/include/safe/iostream.hpp +++ b/include/safe/iostream.hpp @@ -5,5 +5,5 @@ #include auto operator<<(std::ostream &os, safe::any_constrained auto constrained_number) -> std::ostream & { - return os << constrained_number.unsafe_value(); + return os << constrained_number.raw_value(); } diff --git a/test/safe/constrained_number.cpp b/test/safe/constrained_number.cpp index a7db063..8326f1a 100644 --- a/test/safe/constrained_number.cpp +++ b/test/safe/constrained_number.cpp @@ -24,13 +24,13 @@ TYPED_TEST_SUITE(safe_int_assign_test, safe_int_types); TYPED_TEST(safe_int_assign_test, init_vars) { TypeParam value{42_cn}; - EXPECT_EQ(value.unsafe_value(), 42); + EXPECT_EQ(value.raw_value(), 42); } TYPED_TEST(safe_int_assign_test, assign_vars) { TypeParam value{0_cn}; value = 33_cn; - EXPECT_EQ(value.unsafe_value(), 33); + EXPECT_EQ(value.raw_value(), 33); } template class safe_var_ops_test : public testing::Test {}; @@ -44,28 +44,28 @@ TYPED_TEST(safe_var_ops_test, add_op) { safe::constrained_number, TypeParam> const a = 13_cn; safe::constrained_number, TypeParam> const b = 29_cn; auto const result = a + b; - EXPECT_EQ(result.unsafe_value(), 42); + EXPECT_EQ(result.raw_value(), 42); } TYPED_TEST(safe_var_ops_test, minus_op) { safe::constrained_number, TypeParam> const a = 32_cn; safe::constrained_number, TypeParam> const b = 8_cn; auto const result = a - b; - EXPECT_EQ(result.unsafe_value(), 24); + EXPECT_EQ(result.raw_value(), 24); } TYPED_TEST(safe_var_ops_test, multiply_op) { safe::constrained_number, TypeParam> const a = 6_cn; safe::constrained_number, TypeParam> const b = 8_cn; auto const result = a * b; - EXPECT_EQ(result.unsafe_value(), 48); + EXPECT_EQ(result.raw_value(), 48); } TYPED_TEST(safe_var_ops_test, divide_op) { safe::constrained_number, TypeParam> const a = 45_cn; safe::constrained_number, TypeParam> const b = 5_cn; auto const result = a / b; - EXPECT_EQ(result.unsafe_value(), 9); + EXPECT_EQ(result.raw_value(), 9); } // FIXME: MODULO NEEDS TO BE REWRITTEN @@ -73,21 +73,21 @@ TYPED_TEST(safe_var_ops_test, divide_op) { // safe::constrained_number, TypeParam> const a = 12_cn; // safe::constrained_number, TypeParam> const b = 5_cn; // auto const result = a % b; -// EXPECT_EQ(result.unsafe_value(), 2); +// EXPECT_EQ(result.raw_value(), 2); //} // TYPED_TEST(safe_var_ops_test, left_shift_op) { // safe::constrained_number, TypeParam> const a = 8_cn; // safe::constrained_number, TypeParam> const b = 2_cn; // auto const result = a << b; -// EXPECT_EQ(result.unsafe_value(), 32); +// EXPECT_EQ(result.raw_value(), 32); //} // TYPED_TEST(safe_var_ops_test, right_shift_op) { // safe::constrained_number, TypeParam> const a = 48_cn; // safe::constrained_number, TypeParam> const b = 2_cn; // auto const result = a >> b; -// EXPECT_EQ(result.unsafe_value(), 12); +// EXPECT_EQ(result.raw_value(), 12); //} TYPED_TEST(safe_var_ops_test, spaceship_op) { @@ -117,76 +117,76 @@ TYPED_TEST(safe_var_ops_test, max_op) { safe::constrained_number, TypeParam> const a = 48_cn; safe::constrained_number, TypeParam> const b = 32_cn; auto const result = std::max(a, b); - EXPECT_EQ(result.unsafe_value(), 48); + EXPECT_EQ(result.raw_value(), 48); } TYPED_TEST(safe_var_ops_test, min_op) { safe::constrained_number, TypeParam> const a = 48_cn; safe::constrained_number, TypeParam> const b = 32_cn; auto const result = std::min(a, b); - EXPECT_EQ(result.unsafe_value(), 32); + EXPECT_EQ(result.raw_value(), 32); } TYPED_TEST(safe_var_ops_test, clamp_op) { safe::constrained_number, TypeParam> const min = 32_cn; safe::constrained_number, TypeParam> const max = 48_cn; auto const result = clamp(65, min, max); - EXPECT_EQ(result.unsafe_value(), 48); + EXPECT_EQ(result.raw_value(), 48); } TEST(safe_var_test, init_primitive_1) { safe::s64 result{-42}; - EXPECT_EQ(result.unsafe_value(), -42); + EXPECT_EQ(result.raw_value(), -42); } TEST(safe_var_test, init_primitive_2) { safe::s64 result = -42; - EXPECT_EQ(result.unsafe_value(), -42); + EXPECT_EQ(result.raw_value(), -42); } TEST(safe_var_test, assign_primitive) { safe::s64 result{}; result = -42; - EXPECT_EQ(result.unsafe_value(), -42); + EXPECT_EQ(result.raw_value(), -42); } TEST(safe_var_test, init_int_const_1) { safe::s64 result{std::integral_constant{}}; - EXPECT_EQ(result.unsafe_value(), 89); + EXPECT_EQ(result.raw_value(), 89); } TEST(safe_var_test, init_int_const_2) { safe::s64 result = std::integral_constant{}; - EXPECT_EQ(result.unsafe_value(), 89); + EXPECT_EQ(result.raw_value(), 89); } TEST(safe_var_test, assign_int_const) { safe::s64 result{}; result = std::integral_constant{}; - EXPECT_EQ(result.unsafe_value(), -55); + EXPECT_EQ(result.raw_value(), -55); } TEST(safe_var_test, negate_op) { auto const result = -42_cn; - EXPECT_EQ(result.unsafe_value(), -42); + EXPECT_EQ(result.raw_value(), -42); } TEST(safe_var_test, abs_op) { auto const result = abs(-42_cn); - EXPECT_EQ(result.unsafe_value(), 42); + EXPECT_EQ(result.raw_value(), 42); } TEST(safe_var_test, bitwise_or_op) { safe::constrained_number, uint32_t> const a = 15_cn; safe::constrained_number, uint32_t> const b = 9_cn; auto const result = a & b; - EXPECT_EQ(result.unsafe_value(), 9); + EXPECT_EQ(result.raw_value(), 9); } TEST(safe_var_test, add_int_const) { safe::constrained_number, uint32_t> const a = 15_cn; auto const result = a + std::integral_constant{}; - EXPECT_EQ(result.unsafe_value(), 25); + EXPECT_EQ(result.raw_value(), 25); } // FIXME: need to automatically convert integral_constants to safe::constrained_number @@ -195,7 +195,7 @@ TEST(safe_var_test, use_case_bitfield_extract_1) { auto const field = (reg >> u32_<16>)&u32_<0xff>; EXPECT_TRUE(field.constraint <= constrain_mask<0xff>); - EXPECT_EQ(field.unsafe_value(), 0x5e); + EXPECT_EQ(field.raw_value(), 0x5e); } TEST(safe_var_test, use_case_bitfield_extract_2) { @@ -203,5 +203,5 @@ TEST(safe_var_test, use_case_bitfield_extract_2) { auto const field = (reg >> u32_<16>)&u32_<0xff>; EXPECT_TRUE(field.constraint <= constrain_mask<0xff>); - EXPECT_EQ(field.unsafe_value(), 0x5e); + EXPECT_EQ(field.raw_value(), 0x5e); } diff --git a/test/safe/make_constrained.cpp b/test/safe/make_constrained.cpp index 8a54a7e..add1ce8 100644 --- a/test/safe/make_constrained.cpp +++ b/test/safe/make_constrained.cpp @@ -36,4 +36,4 @@ TEST(make_constrained_test, make_with_runtime_check) { TEST(make_constrained_test, make_with_failing_runtime_check) { EXPECT_THAT([]() { (make_constrained>(-10)); }, ThrowsMessage(testing::HasSubstr("make_constrained constraint violation error"))); -} \ No newline at end of file +} diff --git a/test/safe/match.cpp b/test/safe/match.cpp index a11dd5b..0d23c52 100644 --- a/test/safe/match.cpp +++ b/test/safe/match.cpp @@ -27,7 +27,7 @@ class safe_match_test : public ::testing::Test { }; void two_safe_vars(ival_s32<0, 10> a, ival_s32<0, 10> b) { - mock_function_ptr->two_safe_vars(a.unsafe_value(), b.unsafe_value()); + mock_function_ptr->two_safe_vars(a.raw_value(), b.raw_value()); } TEST_F(safe_match_test, simple_pass) { From cec615d105c6e34d7a3e6e3425a5b262c3e14179 Mon Sep 17 00:00:00 2001 From: Luke Valenty Date: Fri, 22 Mar 2024 15:07:27 +0900 Subject: [PATCH 13/13] rename match to match_constraint --- docs/api_reference.adoc | 10 +++--- docs/overview.adoc | 26 +++++++-------- include/safe.hpp | 2 +- .../safe/{match.hpp => match_constraint.hpp} | 8 ++--- test/safe/CMakeLists.txt | 2 +- test/safe/{match.cpp => match_constraint.cpp} | 33 +++++++++++-------- 6 files changed, 43 insertions(+), 38 deletions(-) rename include/safe/{match.hpp => match_constraint.hpp} (90%) rename test/safe/{match.cpp => match_constraint.cpp} (80%) diff --git a/docs/api_reference.adoc b/docs/api_reference.adoc index 174fe0f..8b8f221 100644 --- a/docs/api_reference.adoc +++ b/docs/api_reference.adoc @@ -507,7 +507,7 @@ Identical to `operator[]`. Access element at `pos`. The value of `pos` must be proven to be within the range of the array at compile-time. -=== safe::match +=== safe::match_constraint Use runtime checking to safely encapsulate values into `safe::constrained_number`s. Automatically infer `safe::constrained_number` return type from multiple possible @@ -538,10 +538,10 @@ auto char_to_ord = [](auto digit){ ``` Refactoring this code to use the Safe Arithmetic library is straight-forward -using the `safe::match` function. +using the `safe::match_constraint` function. ```c++ -auto char_to_ord = safe::match( +auto char_to_ord = safe::match_constraint( [](ival_s32<'0', '9'> dec_digit){ return dec_digit - s32_<'0'>; }, @@ -559,7 +559,7 @@ auto char_to_ord = safe::match( This implementation ensures the return type of `char_to_ord` is `ival_s32<0, 15>`. This minimal interval was automatically calculated by -`safe::match`. This is the power of `safe::match`: check runtime values are +`safe::match_constraint`. This is the power of `safe::match_constraint`: check runtime values are within a specified set of values, wrap the values in the corresponding `safe::constrained_number` type, and optionally return a value whose `safe::constrained_number` constraint is the union of the possible values from each branch. @@ -589,7 +589,7 @@ There are some ways around this depending on the specific situation: 1. Use `safe::clamp` to restrict the result. 2. If wraparound is acceptable, use `operator%`. -3. Use `safe::match` to conditionally assign the result. +3. Use `safe::match_constraint` to conditionally assign the result. For a simple `for` loop, these options are not useful. `safe::irange` should be used instead to iterate over a series of integers with a `safe::constrained_number` index diff --git a/docs/overview.adoc b/docs/overview.adoc index b6c1a42..c8fc2b2 100644 --- a/docs/overview.adoc +++ b/docs/overview.adoc @@ -136,15 +136,15 @@ safe::u64 reg_result_3 = (hw_reg_1 + hw_reg2) & 0xFFFF'FFFF'FFFF'FFFF_i; ``` -`safe::match` is the only mechanism in the Safe Arithmetic library that will +`safe::match_constraint` is the only mechanism in the Safe Arithmetic library that will produce additional runtime overhead. It creates a callable object that may be called with `safe::constrained_number` or naked integer values. It uses compile-time checks -if possible to match the given arguments with the `matchable_funcs` arguments. +if possible to match_constraint the given arguments with the `matchable_funcs` arguments. If compile-time checks are not possible for an argument, then the value is checked at runtime to determine if it satisfies the requirements for one of the `matchable_funcs`. It is analogous to a pattern matching switch statement where the `matchable_funcs` arguments `safe::constrained_number` requirements are the patterns to -match the callable object's input arguments against. +match_constraint the callable object's input arguments against. This is the recommended way of marshalling external integer values into a safe arithmetic environment when the valid values are a subset of the underlying @@ -154,7 +154,7 @@ unsigned integer, but the valid values are only 0 through 50,000. The full integer range is valid. ```c++ -// Pseudo C++20 to illustrate safe::match API +// Pseudo C++20 to illustrate safe::match_constraint API template concept Callable = requires(F f, auto... args) { Return retval = f(args...); @@ -163,14 +163,14 @@ concept Callable = requires(F f, auto... args) { namespace safe { template - Callable auto match( + Callable auto match_constraint( Callable auto... matchable_funcs, Callable auto default_func ); } ``` -The operation of `safe::match` is easier to understand with some examples. +The operation of `safe::match_constraint` is easier to understand with some examples. ```c++ // Hardware register reporting a count of some event type. @@ -182,7 +182,7 @@ volatile std::uint32_t event_type_hw_reg; // Firmware array keeping track of updated event counts. safe::array event_counts{}; -constexpr auto process_event_count = safe::match( +constexpr auto process_event_count = safe::match_constraint( []( safe::ival_u32<0, 1023> event_count, safe::ival_u32<0, 16> event_type @@ -198,7 +198,7 @@ constexpr auto process_event_count = safe::match( // Multiple functions with different requirements for parameters may be // passed in. The first function whose argument requirements are satisified // by the runtime argument values is executed. The last function must be - // the default handler and is only executed if no prior match is found. + // the default handler and is only executed if no prior match_constraint is found. [](){ // default action, handle error condition as desired @@ -212,7 +212,7 @@ void event_count_interrupt_handler() { } ``` -`safe::match` is a powerful tool that is discussed in more detail in the +`safe::match_constraint` is a powerful tool that is discussed in more detail in the reference section. The final method of introducing values into the safe arithmetic environment is @@ -241,10 +241,10 @@ void do_something_useful(safe::constrain_interval<0, 1024> useful_value); auto dangerous_value = constraint_cast>(some_function()); do_something_useful(dangerous_value); -// SAFE: Use safe::match instead. This will only call 'do_something_useful' +// SAFE: Use safe::match_constraint instead. This will only call 'do_something_useful' // if the result of 'some_function' satisfies the requirements on -// 'useful_value'. If it doesn't match, the default callable will be invoked. -safe::match(do_something_useful, [](){})(some_function()); +// 'useful_value'. If it doesn't match_constraint, the default callable will be invoked. +safe::match_constraint(do_something_useful, [](){})(some_function()); // SAFE: Don't use constraint_cast(value), try almost everything else first. ``` @@ -381,7 +381,7 @@ stem:[{x in NN \| 0 <= x < 2^n ^^ (x\ \&\ ~V) = C))}] | ```safe::constrain_mask``` | V is the variable bits mask. C is the constant bits mask. `safe::constrain_mask` -produces a set of integers where the binary digits match C if the corresponding +produces a set of integers where the binary digits match_constraint C if the corresponding digits of V are unset. The binary digit places that are set in V are unconstrained in the elements of the produced set. diff --git a/include/safe.hpp b/include/safe.hpp index a77b6f8..19bdce1 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -5,6 +5,6 @@ #include #include #include -#include +#include #include #include diff --git a/include/safe/match.hpp b/include/safe/match_constraint.hpp similarity index 90% rename from include/safe/match.hpp rename to include/safe/match_constraint.hpp index da35f33..38bad8e 100644 --- a/include/safe/match.hpp +++ b/include/safe/match_constraint.hpp @@ -48,7 +48,7 @@ template using common_ret_t = typename common_ret::type; * @return A function object. */ template -[[nodiscard]] constexpr inline auto match(F func, Fs... remaining_funcs) { +[[nodiscard]] constexpr inline auto match_constraint(F func, Fs... remaining_funcs) { using namespace boost::mp11; using func_arg_types = detail::function_args_t; @@ -58,14 +58,14 @@ template return [=](auto &&...args) -> ret_t { if constexpr (sizeof...(remaining_funcs) == 0) { static_assert(mp_size::value == 0, - "Last function in `safe::match` returns the default " + "Last function in `safe::match_constraint` returns the default " "value and must not take any arguments."); return func(); } else { static_assert(mp_size::value == sizeof...(args), - "The number of arguments passed in must match the " + "The number of arguments passed in must match_constraint the " "args in func."); bool const args_satisfy_reqs = detail::check( @@ -76,7 +76,7 @@ template detail::unwrap_var(std::forward(args))}...); } // check the remaining functions' requirements - return match(remaining_funcs...)( + return match_constraint(remaining_funcs...)( std::forward(args)...); } }; diff --git a/test/safe/CMakeLists.txt b/test/safe/CMakeLists.txt index 0cad586..727dc93 100644 --- a/test/safe/CMakeLists.txt +++ b/test/safe/CMakeLists.txt @@ -20,7 +20,7 @@ endfunction() add_test_suites( constrained_number.cpp make_constrained.cpp - match.cpp + match_constraint.cpp array.cpp dsl/add.cpp dsl/divide.cpp diff --git a/test/safe/match.cpp b/test/safe/match_constraint.cpp similarity index 80% rename from test/safe/match.cpp rename to test/safe/match_constraint.cpp index 0d23c52..da94843 100644 --- a/test/safe/match.cpp +++ b/test/safe/match_constraint.cpp @@ -33,45 +33,50 @@ void two_safe_vars(ival_s32<0, 10> a, ival_s32<0, 10> b) { TEST_F(safe_match_test, simple_pass) { EXPECT_CALL(my_mock_function, two_safe_vars(2, 5)).Times(1); - safe::match(two_safe_vars, []() {})(2, 5); + safe::match_constraint(two_safe_vars, []() {})(2, 5); } TEST_F(safe_match_test, pass_with_an_input_var) { EXPECT_CALL(my_mock_function, two_safe_vars(9, 3)).Times(1); - safe::match(two_safe_vars, []() {})(9, 3_cn); + safe::match_constraint(two_safe_vars, []() {})(9, 3_cn); } TEST_F(safe_match_test, pass_with_both_input_vars) { EXPECT_CALL(my_mock_function, two_safe_vars(0, 4)).Times(1); - safe::match(two_safe_vars, []() {})(0_cn, 4_cn); + safe::match_constraint(two_safe_vars, []() {})(0_cn, 4_cn); } TEST_F(safe_match_test, simple_fail) { EXPECT_CALL(my_mock_function, two_safe_vars(_, _)).Times(0); bool fail = false; - safe::match(two_safe_vars, [&]() { fail = true; })(12, 5); + safe::match_constraint(two_safe_vars, [&]() { fail = true; })(12, 5); EXPECT_TRUE(fail); } TEST_F(safe_match_test, fail_with_an_input_var) { EXPECT_CALL(my_mock_function, two_safe_vars(_, _)).Times(0); bool fail = false; - safe::match(two_safe_vars, [&]() { fail = true; })(11, 9_cn); + safe::match_constraint(two_safe_vars, [&]() { fail = true; })(11, 9_cn); EXPECT_TRUE(fail); } TEST_F(safe_match_test, char_to_ord_example) { - auto const char_to_ord = safe::match( - [](ival_s32<'0', '9'> dec_digit) { return dec_digit - s32_<'0'>; }, - [](ival_s32<'a', 'f'> lower_hex_digit) { - return lower_hex_digit - s32_<'a'> + 10_cn; - }, - [](ival_s32<'A', 'F'> upper_hex_digit) { - return upper_hex_digit - s32_<'A'> + 10_cn; - }, - []() { return 0_cn; }); + auto const char_to_ord = + safe::match_constraint( + [](ival_s32<'0', '9'> dec_digit) { + return dec_digit - s32_<'0'>; + }, + [](ival_s32<'a', 'f'> lower_hex_digit) { + return lower_hex_digit - s32_<'a'> + 10_cn; + }, + [](ival_s32<'A', 'F'> upper_hex_digit) { + return upper_hex_digit - s32_<'A'> + 10_cn; + }, + []() { + return 0_cn; + }); EXPECT_EQ(char_to_ord('0'), 0_cn); EXPECT_EQ(char_to_ord('3'), 3_cn);