31 constexpr bool is_exact() const noexcept {
return true; }
32 constexpr std::string_view
spelling() const noexcept {
return spelling_; }
37 std::string_view spelling_;
47 mp_integer() { mpz_init(value); }
48 ~mp_integer() { mpz_clear(value); }
49 mp_integer(mp_integer
const&) =
delete;
50 mp_integer& operator=(mp_integer
const&) =
delete;
53inline void parse_decimal_rational(mpq_ptr result, std::string_view
text)
55 if (
text.
empty())
throw std::invalid_argument(
"exact_rational: empty decimal");
56 std::string unseparated;
57 if (
text.find(
'\'') != std::string_view::npos)
59 for (std::size_t i = 0; i <
text.size(); ++i)
62 unseparated +=
text[i];
63 else if (i == 0 || i + 1 ==
text.size() ||
text[i - 1] <
'0' ||
text[i - 1] >
'9' ||
text[i + 1] <
'0' ||
65 throw std::invalid_argument(
"exact_rational: misplaced digit separator");
71 bool const negative =
text[pos] ==
'-';
72 if (negative ||
text[pos] ==
'+') ++pos;
74 std::size_t fraction_digits = 0;
75 for (; pos <
text.size(); ++pos)
80 if (dot)
throw std::invalid_argument(
"exact_rational: repeated decimal point");
83 else if (
c >=
'0' &&
c <=
'9')
86 if (dot) ++fraction_digits;
91 if (digits.empty())
throw std::invalid_argument(
"exact_rational: expected decimal digits");
93 bool exponent_negative =
false;
94 unsigned long exponent = 0;
95 if (pos <
text.size() && (
text[pos] ==
'e' ||
text[pos] ==
'E'))
98 if (pos <
text.size() && (
text[pos] ==
'+' ||
text[pos] ==
'-')) exponent_negative =
text[pos++] ==
'-';
99 auto const first =
text.data() + pos;
100 auto const last =
text.data() +
text.size();
101 auto const parsed = std::from_chars(first, last, exponent);
102 if (parsed.ec == std::errc::result_out_of_range)
103 throw std::out_of_range(
"exact_rational: decimal exponent is too large");
104 if (parsed.ec != std::errc{} || parsed.ptr != last)
105 throw std::invalid_argument(
"exact_rational: invalid decimal exponent");
108 if (pos !=
text.size())
throw std::invalid_argument(
"exact_rational: invalid decimal spelling");
109 if (fraction_digits > std::numeric_limits<unsigned long>::max())
110 throw std::out_of_range(
"exact_rational: too many fractional digits");
111 auto const fraction =
static_cast<unsigned long>(fraction_digits);
112 if (exponent_negative && exponent > std::numeric_limits<unsigned long>::max() - fraction)
113 throw std::out_of_range(
"exact_rational: decimal scale is too large");
114 bool const denominator = exponent_negative || fraction > exponent;
116 exponent_negative ? fraction + exponent : (denominator ? fraction - exponent : exponent - fraction);
118 constexpr unsigned long max_decimal_scale = 1'000'000;
119 if (
scale > max_decimal_scale)
throw std::out_of_range(
"exact_rational: decimal scale exceeds 1000000");
120 mpz_set_str(mpq_numref(result), digits.c_str(), 10);
121 mpz_set_ui(mpq_denref(result), 1);
122 if (mpz_sgn(mpq_numref(result)) == 0)
return;
124 mpz_ui_pow_ui(factor.value, 10,
scale);
126 mpz_set(mpq_denref(result), factor.value);
128 mpz_mul(mpq_numref(result), mpq_numref(result), factor.value);
129 if (negative) mpz_neg(mpq_numref(result), mpq_numref(result));
130 mpq_canonicalize(result);
133inline void parse_rational(mpq_ptr result, std::string_view
text)
135 auto slash =
text.find(
'/');
136 if (slash == std::string_view::npos)
return parse_decimal_rational(result,
text);
137 auto integer = [](mpz_ptr
out, std::string_view part) {
138 if (part.empty())
throw std::invalid_argument(
"exact_rational: missing fraction component");
139 bool negative = part.front() ==
'-';
140 if (negative || part.front() ==
'+') part.remove_prefix(1);
141 if (part.empty() || part.find_first_not_of(
"0123456789") != std::string_view::npos)
142 throw std::invalid_argument(
"exact_rational: expected integer fraction components");
143 std::string owned(part);
144 mpz_set_str(out, owned.c_str(), 10);
145 if (negative) mpz_neg(out, out);
147 integer(mpq_numref(result),
text.substr(0, slash));
148 integer(mpq_denref(result),
text.substr(slash + 1));
149 if (mpz_sgn(mpq_denref(result)) == 0)
throw std::domain_error(
"exact_rational: zero denominator");
150 mpq_canonicalize(result);
169 constexpr bool initialized() const noexcept {
return initialized_; }
175 template <std::
integral I>
176 requires(!std::same_as<I, bool>)
179 char text[std::numeric_limits<I>::digits10 + 4];
182 mpz_set_str(mpq_numref(value_),
text, 10);
187 if (other.initialized_)
191 mpq_set(value_, other.value_);
203 if (initialized_) mpq_clear(value_);
209 std::swap(value_[0], other.value_[0]);
210 std::swap(initialized_, other.initialized_);
220 if (!this->
initialized())
throw std::logic_error(
"exact_rational: numerical use of an unset value");
228 std::string
text(mpz_sizeinbase(mpq_numref(
value), 10) + mpz_sizeinbase(mpq_denref(
value), 10) + 4,
'\0');
230 text.resize(std::char_traits<char>::length(
text.c_str()));
242 friend struct detail::mpreal_access;
244 bool initialized_ =
false;
268 if (mpq_sgn(divisor) == 0)
throw std::domain_error(
"exact_rational: division by zero");
298 if (
degree == 0)
throw std::domain_error(
"exact_rational: zeroth root");
300 bool negative = mpq_sgn(q) < 0;
301 if (negative &&
degree % 2 == 0)
return std::nullopt;
302 detail::mp_integer magnitude;
303 mpz_abs(magnitude.value, mpq_numref(q));
305 if (!mpz_root(mpq_numref(result.value_), magnitude.value,
degree) ||
306 !mpz_root(mpq_denref(result.value_), mpq_denref(q),
degree))
308 if (negative) mpz_neg(mpq_numref(result.value_), mpq_numref(result.value_));
315template <
class T> T integer_power(T base, mpz_srcptr exponent)
317 mp_integer remaining;
318 mpz_abs(remaining.value, exponent);
320 if (mpz_sgn(exponent) < 0) base = result / base;
321 while (mpz_sgn(remaining.value) != 0)
323 if (mpz_odd_p(remaining.value)) result = result * base;
324 mpz_fdiv_q_2exp(remaining.value, remaining.value, 1);
325 if (mpz_sgn(remaining.value) != 0) base = base * base;
334template <
class X,
class Y>
335 requires((std::same_as<X, exact_rational> || std::same_as<X, decimal_literal>) &&
336 (std::same_as<Y, exact_rational> || std::same_as<Y, decimal_literal>))
340 (void)x.native_handle();
342 auto degree = mpq_denref(exponent);
343 if (mpz_cmp_ui(
degree, 1) == 0)
return detail::integer_power(x, mpq_numref(exponent));
344 if (x == 0 || x == 1 || (x == -1 && mpz_odd_p(
degree)))
return detail::integer_power(x, mpq_numref(exponent));
347 if (mpz_fits_ulong_p(
degree))
348 if (
auto root =
rational_root(x, mpz_get_ui(
degree)))
return detail::integer_power(*root, mpq_numref(exponent));
349 throw std::logic_error(
"exact_rational: power requires finite working precision");
354 requires(std::same_as<X, exact_rational> || std::same_as<X, decimal_literal>)
359 throw std::logic_error(
"exact_rational: square root requires finite working precision");
365 (std::integral<std::remove_cvref_t<T>> && !std::same_as<std::remove_cvref_t<T>,
bool>) ||
366 std::same_as<std::remove_cvref_t<T>, exact_rational> || std::same_as<std::remove_cvref_t<T>, decimal_literal>;
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
A precisionless decimal spelling produced by the _mp literal operator.
Definition exact_rational.hpp:27
constexpr std::string_view spelling() const noexcept
Definition exact_rational.hpp:32
mpreal at(Precision precision) const
Materialize the literal with one rounding to the requested precision.
Definition mpreal.hpp:554
constexpr bool is_exact() const noexcept
Definition exact_rational.hpp:31
Owning arbitrary-size rational with exact, eager arithmetic.
Definition exact_rational.hpp:161
void swap(exact_rational &other) noexcept
Exchange payload ownership, including unset states, without allocating.
Definition exact_rational.hpp:206
std::string to_string() const
Return the canonical numerator/denominator, or an integer when exact.
Definition exact_rational.hpp:225
constexpr bool initialized() const noexcept
Whether this object owns a rational value rather than being unset.
Definition exact_rational.hpp:169
friend exact_rational operator/(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:265
exact_rational(exact_rational &&other) noexcept
Transfer the payload without allocating, leaving the source unset.
Definition exact_rational.hpp:195
exact_rational & operator=(exact_rational other) noexcept
Definition exact_rational.hpp:196
exact_rational(std::string_view text)
Definition exact_rational.hpp:172
friend exact_rational operator-(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:253
exact_rational()
Definition exact_rational.hpp:163
constexpr bool is_exact() const noexcept
Whether this object carries an exact value; false when unset.
Definition exact_rational.hpp:171
exact_rational(exact_rational const &other)
Definition exact_rational.hpp:185
friend exact_rational operator*(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:259
~exact_rational()
Definition exact_rational.hpp:201
exact_rational(I value)
Definition exact_rational.hpp:177
mpreal at(Precision precision) const
Convert to a real value with one rounding at explicit precision.
Definition mpreal.hpp:553
friend exact_rational operator+(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:247
mpq_srcptr native_handle() const
Borrow the read-only GMP rational while this object retains its payload.
Definition exact_rational.hpp:218
friend std::optional< exact_rational > rational_root(exact_rational const &, unsigned long)
Return a rational nth root when it exists, without choosing a precision.
Definition exact_rational.hpp:296
exact_rational(decimal_literal literal)
Definition exact_rational.hpp:173
Owning exact rational or finite-precision MPFR real, with eager arithmetic.
Definition mpreal.hpp:74
bool empty() const noexcept
Return whether this document has no spans.
Definition presentation.cpp:1182
Exact numeric sources accepted without an approximation precision.
Definition exact_rational.hpp:364
locale_t value
Definition data_table.cpp:49
presentation::styled_text text
Definition display.cpp:141
Scalar dot(std::span< Scalar const > x, std::span< Scalar const > y)
Compute a BLAS-style real dot product.
Definition dense_linalg.hpp:240
Definition async/async.hpp:26
constexpr auto operator*(basic_half_int< T > lhs, U rhs) -> basic_half_int< T >
Multiply a half-integer value by an integral scalar.
Definition half_int.hpp:367
bool operator==(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:284
exact_rational sqrt(X const &value)
Return the nonnegative rational square root, or require finite precision.
Definition exact_rational.hpp:355
auto degree(QNum const &q) -> int
Return the integral degree of a quantum number.
Definition qnum.hpp:270
constexpr auto operator-(basic_half_int< T > lhs, basic_half_int< T > rhs) -> basic_half_int< T >
Subtract two half-integer values.
Definition half_int.hpp:345
std::optional< exact_rational > rational_root(exact_rational const &x, unsigned long degree)
Return a rational nth root when it exists, without choosing a precision.
Definition exact_rational.hpp:296
exact_rational pow(X const &base, Y const &power)
Evaluate a rational power exactly when its real result is rational.
Definition exact_rational.hpp:337
void scale(Tensor &tensor, Scalar factor)
Multiply every stored numerical block by a scalar in place.
Definition block_tensor_linear.hpp:400
constexpr auto operator/(basic_half_int< T > lhs, U rhs) -> U
Divide a half-integer value by a floating-point scalar.
Definition half_int.hpp:422
std::strong_ordering operator<=>(exact_rational const &a, exact_rational const &b)
Definition exact_rational.hpp:288