11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
21PROMEKI_NAMESPACE_BEGIN
47template <
typename T =
int>
class Rational {
57 Rational(T n = 0, T d = 1) : _num(n), _den(d) {
58 if (_den != 0) simplify();
62 bool isValid()
const {
return _den != 0; }
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);
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);
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);
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);
113 bool operator==(
const Rational &rhs)
const {
return _num == rhs._num && _den == rhs._den; }
116 bool operator!=(
const Rational &rhs)
const {
return !(*
this == rhs); }
119 T numerator()
const {
return _num; }
122 T denominator()
const {
return _den; }
128 double toDouble()
const {
129 if (!isValid())
return 0.0;
130 return static_cast<double>(_num) /
static_cast<double>(_den);
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);
155 return makeResult(Rational<T>(
static_cast<T
>(rounded), T(1)));
159 String toString()
const {
return String::dec(_num) +
"/" + String::dec(_den); }
162 operator String()
const {
return toString(); }
169 T gcd = std::gcd(_num, _den);
186template <
typename T>
struct std::formatter<promeki::Rational<T>> : promeki::ToStringFormatter<promeki::Rational<T>> {};