Skip to content

Commit 683b461

Browse files
committed
code(numbers): Created the 'Number' concept to determine whenever a type satisfies the criteria to be a Number
code(numbers): implemented the basic operations for Rationals code(numbers): Implemented the implementation of the equality operators for Naturals, Integers and Rationals code(numbers): implemented some operator overloads for mix arithmetic operations between types code(tsuite-assertions): cleaned the assertions module, and refactored the way of how are assertions handled internally code(zero::concepts): Created a set of concepts to constrain types that are printable
1 parent 569e987 commit 683b461

9 files changed

Lines changed: 202 additions & 68 deletions

File tree

zero/ifc/commons/concepts.cppm

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ import type_traits;
1212

1313
export namespace zero::concepts {
1414

15+
/// \brief Constrain for types that has an operator<< overload
16+
template <typename T>
17+
concept Ostreamable = requires(const T& t, std::ostream& os) {
18+
{ os << t } -> std::same_as<std::ostream&>;
19+
};
20+
21+
/// \brief Constrain for types that has an std::to_string implementation
22+
template <typename T>
23+
concept StringConvertible = requires(const T& t) {
24+
{ std::to_string(t) } -> std::same_as<std::string>;
25+
};
26+
27+
/// \brief Constrain for types with either operator<< or std::to_string
28+
template <typename T>
29+
concept Printable = Ostreamable<T> || StringConvertible<T>;
30+
1531
/**
1632
* @brief checks if the type is a type with a push_back
1733
* member method implemented

zero/ifc/iterators/internal/iterator_detail.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ namespace iterator::__detail {
1717
struct arrow_proxy {
1818
Reference r;
1919

20-
arrow_proxy(Reference&& value) : r(std::move(value)) {}
20+
explicit arrow_proxy(Reference&& value) : r(std::move(value)) {}
2121
Reference* operator->() const { return &r; }
2222
};
2323

2424
/**
2525
* @brief The iterator declares itself a single pass iterator.
26-
* input or output iterators must meet this requirement
26+
* input or output iterators must meet this requirement
2727
*/
2828
template <typename T>
2929
concept is_single_pass = bool(T::single_pass_iterator);
@@ -62,7 +62,7 @@ namespace iterator::__detail {
6262
template <typename T>
6363
using infer_difference_type_t = typename infer_difference_type<T>::type;
6464

65-
/// Partial template speciallizations to allow us to deduce the
65+
/// Partial template specializations to allow us to deduce the
6666
/// iterator mandatory `value:type`
6767
template <typename T>
6868
requires requires { typename T::value_type; }
@@ -102,7 +102,7 @@ namespace iterator::__detail {
102102

103103
/// Helper concept to declare a later-deduced parameter type,
104104
/// but that type is still constrained to be a type that we
105-
///don’t yet know
105+
/// don’t yet know
106106
template <typename Arg, typename Iter>
107107
concept difference_type_arg =
108108
std::convertible_to<Arg, infer_difference_type_t<Iter>>;

zero/ifc/iterators/iterator.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
export module iterator;
77

8-
export import :concepts;
8+
export import :concepts; ///< Iterator concepts
99

1010
export import :input_iterator;
1111
export import :iterator_facade;

zero/ifc/iterators/legacy/legacy_output_iterator.cppm

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export namespace zero::iterator::legacy
2929
struct output_iter_proxy {
3030
output_iter<Z> &_iter;
3131

32-
constexpr output_iter_proxy(output_iter<Z> &iter) noexcept : _iter(iter) {}
32+
constexpr explicit output_iter_proxy(output_iter<Z> &iter) noexcept : _iter(iter) {}
3333

3434
template <typename U>
3535
constexpr auto operator=(const U &val) -> output_iter_proxy& {
@@ -49,8 +49,8 @@ export namespace zero::iterator::legacy
4949
};
5050

5151
public:
52-
constexpr output_iter<T>() noexcept = default;
53-
constexpr output_iter(T &elem) noexcept : _elem(&elem) {}
52+
constexpr output_iter<T>() noexcept = default; // Left defaulted and not deleted because the legacy implementation
53+
constexpr explicit output_iter(T& elem) noexcept : _elem(&elem) {}
5454

5555
constexpr output_iter<T>(const output_iter<T> &other) = default;
5656
constexpr output_iter<T>(output_iter<T> &&other) noexcept = default;
@@ -88,8 +88,7 @@ export namespace zero::iterator::legacy
8888

8989
[[nodiscard]]
9090
constexpr friend auto operator!=(const output_iter& lhs, const output_iter& rhs) noexcept -> bool {
91-
return not (lhs == rhs);
91+
return lhs != rhs;
9292
}
9393
};
94-
9594
}

zero/ifc/math/numbers.cppm

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ export namespace zero::math {
4848
[[nodiscard]] inline constexpr Natural operator-(Natural rhs) const noexcept;
4949
[[nodiscard]] inline constexpr Natural operator*(Natural rhs) const noexcept;
5050
[[nodiscard]] inline constexpr Rational operator/(Natural rhs) const noexcept;
51+
// Comparison operator overloads
52+
[[nodiscard]] inline constexpr bool operator==(Natural rhs) const noexcept;
53+
[[nodiscard]] inline constexpr bool operator==(unsigned int rhs) const noexcept;
54+
// Printable
55+
inline constexpr friend std::ostream& operator<<(std::ostream& os, const Natural& rhs) {
56+
os << rhs._number;
57+
return os;
58+
}
5159
};
5260

5361
/// A whole real number
@@ -70,10 +78,17 @@ export namespace zero::math {
7078
[[nodiscard]] inline constexpr Integer operator*(Integer rhs) const noexcept;
7179
[[nodiscard]] inline constexpr Rational operator*(Rational rhs) const noexcept;
7280
[[nodiscard]] inline constexpr Rational operator/(Integer rhs) const noexcept;
81+
// Comparison operator overloads
7382
[[nodiscard]] inline constexpr bool operator==(Integer rhs) const noexcept;
83+
[[nodiscard]] inline constexpr bool operator==(int rhs) const noexcept;
7484

7585
// Explicit conversion operators
7686
[[nodiscard]] inline explicit operator int() const { return _number; }
87+
// Printable
88+
inline constexpr friend std::ostream& operator<<(std::ostream& os, const Integer& rhs) {
89+
os << rhs._number;
90+
return os;
91+
}
7792
};
7893

7994
/// @brief A type that represents rational numbers of the form: ℚ = {a, b ∈ ℤ, b ≠ 0}
@@ -99,6 +114,8 @@ export namespace zero::math {
99114
Integer _numerator; ///< The numerator of the rational number, belonging to ℤ.
100115
Integer _denominator; ///< The denominator of the rational number, belonging to ℤ, NOT excluding the zero.
101116
public:
117+
static constexpr MathSymbol symbol = MathSymbol::Rationals;
118+
102119
[[nodiscard]] constexpr Rational(int numerator, int denominator) noexcept
103120
: _numerator(numerator), _denominator(denominator) {}
104121
[[nodiscard]] constexpr Rational(Natural numerator, Natural denominator) noexcept
@@ -113,6 +130,16 @@ export namespace zero::math {
113130

114131
// Arithmetic operator overloads
115132
[[nodiscard]] inline constexpr Rational operator+(Rational rhs) const;
133+
// TODO complete arithmetic overloads
134+
// Comparison operator overloads
135+
[[nodiscard]] inline constexpr bool operator==(Rational rhs) const noexcept;
136+
// Printable
137+
inline constexpr friend std::ostream& operator<<(std::ostream& os, const Rational& rhs) {
138+
os << rhs._numerator;
139+
os << 0x2044;
140+
os << rhs._denominator;
141+
return os;
142+
}
116143
};
117144

118145

@@ -132,6 +159,7 @@ using namespace zero::math;
132159

133160
/*++++++++ Operator overloads implementations ++++++++++*/
134161
/*+++++++++++++++++ Naturals +++++++++++++++++*/
162+
// Arithmetic
135163
[[nodiscard]] inline constexpr Natural Natural::operator+(const Natural rhs) const noexcept {
136164
return Natural(_number + rhs.number());
137165
}
@@ -146,8 +174,16 @@ using namespace zero::math;
146174
[[nodiscard]] inline constexpr Rational Natural::operator/(const Natural rhs) const noexcept {
147175
return {static_cast<signed int>(_number), static_cast<signed int>(rhs.number())};
148176
}
177+
// Equality
178+
[[nodiscard]] inline constexpr bool Natural::operator==(const Natural rhs) const noexcept {
179+
return _number == rhs.number();
180+
}
181+
[[nodiscard]] inline constexpr bool Natural::operator==(const unsigned int rhs) const noexcept {
182+
return _number == rhs;
183+
}
149184

150-
/*+++++++++++++++++ Integers +++++++++++++++++*/
185+
/*+++++++++++++++++ Integers +++++++++++++++++*/
186+
// Arithmetic
151187
[[nodiscard]] inline constexpr Integer Integer::operator+(const Integer rhs) const noexcept {
152188
return Integer(_number + rhs.number());
153189
}
@@ -163,11 +199,17 @@ using namespace zero::math;
163199
[[nodiscard]] inline constexpr Rational Integer::operator/(const Integer rhs) const noexcept {
164200
return {static_cast<signed int>(_number), static_cast<signed int>(rhs.number())};
165201
}
202+
// Equality
166203
[[nodiscard]] inline constexpr bool Integer::operator==(const Integer rhs) const noexcept {
167204
return _number == rhs.number();
168205
}
206+
[[nodiscard]] inline constexpr bool Integer::operator==(const int rhs) const noexcept {
207+
return _number == rhs;
208+
}
169209

170210
/*+++++++++++++++++ Rationals +++++++++++++++++*/
211+
// Arithmetic
212+
171213
/// Adds the current rational number to another rational number.
172214
/// @param rhs The rational number to be added.
173215
/// @return The sum of the two rational numbers.
@@ -177,17 +219,18 @@ using namespace zero::math;
177219
/// finds the least common multiple (LCM) of the denominators and scales the
178220
/// numerators to have the LCM as the common denominator before adding.
179221
[[nodiscard]] inline constexpr Rational Rational::operator+(const Rational rhs) const {
180-
if (_denominator == rhs.denominator())
222+
if (_denominator == rhs.denominator()) // Like fractions
181223
return {
182224
static_cast<int>(_numerator) + static_cast<int>(rhs.numerator()),
183225
static_cast<int>(_denominator)
184-
}; // Like fractions
185-
else {
226+
};
227+
else { // Unlike fractions
186228
const int lhs_numerator = static_cast<int>(_numerator);
187229
const int rhs_numerator = static_cast<int>(rhs._numerator);
188230
const int lhs_denominator = static_cast<int>(_denominator);
189231
const int rhs_denominator = static_cast<int>(rhs._denominator);
190232

233+
// Get their lcd by finding their lcm
191234
const auto lcd = zero::math::lcm(_denominator.number(), rhs.denominator().number());
192235

193236
// Scale numerators to have the common denominator (lcm)
@@ -196,3 +239,27 @@ using namespace zero::math;
196239
return {numerator, lcd};
197240
}
198241
}
242+
243+
// Equality
244+
245+
// TODO should we check that 4/2 is the same as 2/1 right? Or we should maintain the difference and explictly
246+
// say that 4/2 aren't the same Rational number as 2/1?
247+
[[nodiscard]] inline constexpr bool Rational::operator==(const Rational rhs) const noexcept {
248+
return _numerator == rhs.numerator() && _denominator == rhs.denominator();
249+
}
250+
251+
/*+++++++++++++++++ oss operator overloads (oss) +++++++++++++++++*/
252+
//inline constexpr std::ostream& Natural::operator<<(std::ostream& os) {
253+
// os << _number;
254+
// return os;
255+
//}
256+
//inline constexpr std::ostream& Integer::operator<<(std::ostream& os) {
257+
// os << _number;
258+
// return os;
259+
//}
260+
//inline constexpr std::ostream& Rational::operator<<(std::ostream& os) {
261+
// os << _numerator;
262+
// os << 0x2044;
263+
// os << _denominator;
264+
// return os;
265+
//}
Lines changed: 91 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,109 @@
11
export module tsuite:assertions;
22

33
import std;
4+
import concepts;
5+
6+
using namespace zero::concepts;
7+
8+
template <Printable T, Printable U>
9+
constexpr inline void throw_on_failed_test(const T& expected, const U& actual);
410

511
export {
6-
/**
7-
* Compares two values. Generates a test failed if values are non-equal.
8-
*/
9-
template<typename T>
10-
requires (!std::is_pointer_v<T>)
11-
void assertEquals(const T& expected, const T& actual) {
12-
if (expected != actual)
13-
throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) +
14-
", actual = " + std::to_string(actual));
12+
/// @brief Compares two values. Generates a test failed if values are non-equal.
13+
/// @param actual the given or computed value
14+
/// @param expected the given expectation for the computed value
15+
template<typename T, typename U>
16+
requires (!std::is_pointer_v<T> && !std::is_pointer_v<U>)
17+
constexpr void assertEquals(const T& expected, const U& actual) {
18+
::throw_on_failed_test(expected, actual);
1519
}
1620

17-
/**
18-
* Compares two values being T pointer types.
19-
* Generates a test failed if the values after dereference
20-
* the pointers are non-equal.
21-
*/
21+
/// @brief Compares two values being T pointer types.
22+
/// Generates a test failed if the values after dereference the pointers are non-equal.
2223
template<typename T>
23-
void assertEquals(const T* expected_ptr, const T* actual_ptr) {
24-
auto expected = *expected_ptr;
25-
auto actual = *actual_ptr;
24+
constexpr void assertEquals(const T* expected_ptr, const T* actual_ptr) {
25+
const auto expected = *expected_ptr;
26+
const auto actual = *actual_ptr;
2627

27-
if (expected != actual)
28-
throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) +
29-
", actual = " + std::to_string(actual));
28+
::throw_on_failed_test(expected, actual);
3029
}
3130

32-
/**
33-
* Compares two values. Generates a test failed if the values are equals.
34-
*/
31+
32+
/// @brief Compares two values. Generates a test failed if the values are equals.
3533
template<typename T>
3634
requires (!std::is_pointer_v<T>)
37-
void assertNotEquals(const T& expected, const T& actual) {
38-
if (expected == actual)
39-
throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) +
40-
", actual = " + std::to_string(actual));
35+
constexpr void assertNotEquals(const T& expected, const T& actual) {
36+
::throw_on_failed_test(expected, actual);
4137
}
4238

43-
/**
44-
* Compares two values being T pointer types.
45-
* Generates a test failed if the values after dereference
46-
* the pointers are non-equal.
47-
*/
39+
40+
/// @brief Compares two values being T pointer types.
41+
/// Generates a test failed if the values after dereference
42+
/// the pointers are non-equal.
4843
template<typename T>
49-
void assertNotEquals(const T* expected_ptr, const T* actual_ptr) {
50-
auto expected = *expected_ptr;
51-
auto actual = *actual_ptr;
44+
constexpr void assertNotEquals(const T* expected_ptr, const T* actual_ptr) {
45+
const auto expected = *expected_ptr;
46+
const auto actual = *actual_ptr;
47+
48+
::throw_on_failed_test(expected, actual);
49+
}
50+
}
51+
52+
// Detail -- TODO move to an impl module partition
53+
54+
/// \brief custom internal exception for the assertions failure
55+
class assertion_failed : public std::exception {
56+
private:
57+
std::string message_;
5258

53-
if (expected == actual)
54-
throw std::runtime_error("Assertion failed: expected = " + std::to_string(expected) +
55-
", actual = " + std::to_string(actual));
59+
public:
60+
explicit assertion_failed(const std::string& message)
61+
: message_(message) {}
62+
63+
[[nodiscard]] const char* what() const noexcept override {
64+
return message_.c_str();
65+
}
66+
};
67+
68+
/// \brief helper to reduce cognitive complexity,
69+
/// \enabled when the concept {\link @StringConvertible} is satisfied
70+
template<typename T, typename U>
71+
constexpr inline void throw_on_failed_test_str_impl(const T& expected, const U& actual) {
72+
if (expected != actual) {
73+
const auto expected_str = std::to_string(expected);
74+
const auto actual_str = std::to_string(actual);
75+
76+
std::string msg {};
77+
msg.reserve(29 + expected_str.size() + actual_str.size() + 11);
78+
79+
msg += "Assertion failed: expected = "; // 29
80+
msg += expected_str;
81+
msg += ", actual = "; // 11
82+
msg += actual_str;
83+
84+
throw assertion_failed(msg);
5685
}
57-
}
86+
}
87+
88+
/// \brief helper to reduce cognitive complexity,
89+
/// \enabled when the concept {\link @Ostreamable} is satisfied
90+
template<typename T, typename U>
91+
constexpr inline void throw_on_failed_test_oss_impl(const T& expected, const U& actual) {
92+
if (expected != actual) {
93+
std::ostringstream oss;
94+
oss << "Assertion failed: expected = ";
95+
oss << expected;
96+
oss << ", actual = ";
97+
oss << actual;
98+
99+
throw assertion_failed(oss.str()); // TODO use a better one
100+
}
101+
}
102+
103+
template <Printable T, Printable U>
104+
constexpr inline void throw_on_failed_test(const T& expected, const U& actual) {
105+
if constexpr (StringConvertible<T> && StringConvertible<U>)
106+
throw_on_failed_test_str_impl(expected, actual);
107+
else
108+
throw_on_failed_test_oss_impl(expected, actual);
109+
}

0 commit comments

Comments
 (0)