libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
rational.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <algorithm>
14#include <cmath>
15#include <limits>
16#include <numeric>
17#include <promeki/namespace.h>
18#include <promeki/result.h>
19#include <promeki/string.h>
20
21PROMEKI_NAMESPACE_BEGIN
22
47template <typename T = int> class Rational {
48 public:
57 Rational(T n = 0, T d = 1) : _num(n), _den(d) {
58 if (_den != 0) simplify();
59 }
60
62 bool isValid() const { return _den != 0; }
63
69 Rational operator+(const Rational &rhs) const {
70 if (!isValid() || !rhs.isValid()) return Rational(0, 0);
71 T lcm = std::lcm(_den, rhs._den);
72 T num = _num * (lcm / _den) + rhs._num * (lcm / rhs._den);
73 return Rational(num, lcm);
74 }
75
81 Rational operator-(const Rational &rhs) const {
82 if (!isValid() || !rhs.isValid()) return Rational(0, 0);
83 T lcm = std::lcm(_den, rhs._den);
84 T num = _num * (lcm / _den) - rhs._num * (lcm / rhs._den);
85 return Rational(num, lcm);
86 }
87
93 Rational operator*(const Rational &rhs) const {
94 if (!isValid() || !rhs.isValid()) return Rational(0, 0);
95 T num = _num * rhs._num;
96 T den = _den * rhs._den;
97 return Rational(num, den);
98 }
99
105 Rational operator/(const Rational &rhs) const {
106 if (!isValid() || !rhs.isValid() || rhs._num == 0) return Rational(0, 0);
107 T num = _num * rhs._den;
108 T den = _den * rhs._num;
109 return Rational(num, den);
110 }
111
113 bool operator==(const Rational &rhs) const { return _num == rhs._num && _den == rhs._den; }
114
116 bool operator!=(const Rational &rhs) const { return !(*this == rhs); }
117
119 T numerator() const { return _num; }
120
122 T denominator() const { return _den; }
123
128 double toDouble() const {
129 if (!isValid()) return 0.0;
130 return static_cast<double>(_num) / static_cast<double>(_den);
131 }
132
147 static Result<Rational<T>> fromDouble(double val) {
148 if (!std::isfinite(val)) return makeError<Rational<T>>(Error::ParseFailed);
149 double rounded = std::nearbyint(val);
150 if (rounded != val) return makeError<Rational<T>>(Error::ParseFailed);
151 if (rounded < static_cast<double>(std::numeric_limits<T>::lowest()) ||
152 rounded > static_cast<double>(std::numeric_limits<T>::max())) {
153 return makeError<Rational<T>>(Error::OutOfRange);
154 }
155 return makeResult(Rational<T>(static_cast<T>(rounded), T(1)));
156 }
157
159 String toString() const { return String::dec(_num) + "/" + String::dec(_den); }
160
162 operator String() const { return toString(); }
163
164 private:
165 T _num;
166 T _den;
167
168 void simplify() {
169 T gcd = std::gcd(_num, _den);
170 _num /= gcd;
171 _den /= gcd;
172 return;
173 }
174};
175
176PROMEKI_NAMESPACE_END
177
186template <typename T> struct std::formatter<promeki::Rational<T>> : promeki::ToStringFormatter<promeki::Rational<T>> {};
187
188#endif // PROMEKI_ENABLE_CORE