C++ Essence Library 0.1.0
A Utility Library for Modern C++ Programming
Loading...
Searching...
No Matches
rational.hpp
1/*
2 * Copyright (c) 2024 The RefValue Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#pragma once
24
25#include <concepts>
26#include <cstddef>
27#include <cstdint>
28#include <numeric>
29#include <type_traits>
30#include <utility>
31
32namespace essence {
36 template <typename T>
37 concept similar_rational = requires(std::int64_t a, std::int64_t b) {
38 T{a, b};
39 {
40 [](T inner) {
41 auto&& [x, y] = inner;
42 constexpr std::size_t type_1 = std::same_as<std::decay_t<decltype(x)>, std::int64_t> ? 1U : 0U;
43 constexpr std::size_t type_2 = std::same_as<std::decay_t<decltype(y)>, std::int64_t> ? 1U : 0U;
44
45 return std::integral_constant<std::size_t, type_1 + type_2>{};
46 }(T{})
47 } -> std::same_as<std::integral_constant<std::size_t, 2U>>;
48 };
49
53 struct rational {
54 std::int64_t numerator{};
55 std::int64_t denominator{};
56
57 constexpr rational() noexcept = default;
58
59 constexpr rational(const rational&) = default;
60
61 constexpr rational(rational&&) noexcept = default;
62
63 explicit constexpr rational(std::int64_t numerator) noexcept : rational{numerator, 1} {}
64
65 constexpr rational(std::int64_t numerator, std::int64_t denominator) noexcept
66 : numerator{numerator}, denominator{denominator} {
67 simplify();
68 }
69
70 template <typename T>
71 requires(std::is_aggregate_v<std::decay_t<T>> && similar_rational<std::decay_t<T>>)
72 explicit constexpr rational(T&& obj) noexcept : rational{} {
73 auto&& [numerator, denominator] = std::forward<T>(obj);
74
75 this->numerator = numerator;
76 this->denominator = denominator;
77 simplify();
78 }
79
80 rational& operator=(const rational&) = default;
81
82 rational& operator=(rational&&) noexcept = default;
83
88 template <typename T>
90 constexpr operator T() const noexcept(similar_rational<std::decay_t<T>>) { // NOLINT(*-explicit-constructor)
91 return std::decay_t<T>{numerator, denominator};
92 }
93
98 template <std::floating_point T>
99 explicit constexpr operator T() const noexcept {
100 return static_cast<T>(numerator) / static_cast<T>(denominator);
101 }
102
105 constexpr void simplify() noexcept {
106 const auto gcd = std::gcd(numerator, denominator);
107
108 numerator /= gcd;
109 denominator /= gcd;
110
111 // A/-B --> -A/B
112 // -A/-B --> A/B
113 if (denominator < 0) {
114 numerator = -numerator;
115 denominator = -denominator;
116 }
117 }
118
123 [[nodiscard]] constexpr rational reciprocal() const noexcept {
124 return rational{denominator, numerator};
125 }
126
127 constexpr bool operator<(const rational& right) const noexcept {
128 return numerator * right.denominator < right.numerator * denominator;
129 }
130
131 constexpr bool operator>(const rational& right) const noexcept {
132 return numerator * right.denominator > right.numerator * denominator;
133 }
134
135 constexpr bool operator==(const rational& right) const noexcept {
136 return numerator * right.denominator == right.numerator * denominator;
137 }
138
139 constexpr bool operator!=(const rational& right) const noexcept {
140 return !(*this == right);
141 }
142
143 constexpr bool operator<=(const rational& right) const noexcept {
144 return !(*this > right);
145 }
146
147 constexpr bool operator>=(const rational& right) const noexcept {
148 return !(*this < right);
149 }
150
151 friend constexpr rational operator-(const rational& value) noexcept {
152 return rational{-value.numerator, value.denominator};
153 }
154
155 friend constexpr rational operator+(const rational& left, const rational& right) noexcept {
156 const auto lcm = std::lcm(left.denominator, right.denominator);
157 rational result{
158 left.numerator * (lcm / left.denominator) + right.numerator * (lcm / right.denominator), lcm};
159
160 return (result.simplify(), result);
161 }
162
163 friend constexpr rational operator-(const rational& left, const rational& right) noexcept {
164 return left + (-right);
165 }
166
167 friend constexpr rational operator*(const rational& left, const rational& right) noexcept {
168 rational result{left.numerator * right.numerator, left.denominator * right.denominator};
169
170 return (result.simplify(), result);
171 }
172
173 friend constexpr rational operator/(const rational& left, const rational& right) noexcept {
174 return left * right.reciprocal();
175 }
176 };
177
183 constexpr std::int64_t ceil(rational number) noexcept {
184 return (number.numerator + number.denominator - 1) / number.denominator;
185 }
186} // namespace essence
Checks if an object is similar to essence::rational.
Definition rational.hpp:37
@ value
the parser finished reading a JSON value
Represents a rational number consisting of its corresponding numerator and denominator.
Definition rational.hpp:53
constexpr rational reciprocal() const noexcept
Gets the reciprocal of the rational.
Definition rational.hpp:123