Dedekind 0.0.0
A structuralist C++23 Module Library
Loading...
Searching...
No Matches
dedekind::numbers::Rational< Z > Class Template Referenceexport

The Field of Fractions over an Integral Domain Z. More...

Public Types

using Domain = Rational
using IntegerCarrier = Z
using cardinality_type = ℵ_0
 Cardinality class of the ℚ carrier: ℵ_0 (#848/#927). ℚ = (ℤ × ℤ_≠0)/~ is countable, and this matches the ambient's own tag (ℚ = 𝔸<Rational<default_integer>> carries C = ℵ_0 below). A point-free halfspace {x∈ℚ & π>c} is carved in :order, which is upstream of :numbers and so cannot recognise a countable non-integral carrier structurally (IsRingIntegral is false on ℚ); the carrier therefore self-declares, and order::Halfspace reads this to classify identically to the scout comprehension (which inherits C directly). Mirrors ExtensionalCardinal's own cardinality_type (:sets:cardinality).

Public Member Functions

constexpr Rational ()
 Default: 0/1, the value-initialised T{} of the generic witnesses.
constexpr Rational (Z num, Z den)
constexpr Rational (Z n)
 Embedding of an integer as a rational n/1. Implicit to allow Rational<Z>{Z_value} to flow into the (a + b) / T{2} midpoint expression that the IsDense concept (in order:completeness) requires. When T = Rational<Z>, T{2} resolves through the Rational(std::integral) overload below (int → Rational<Z> in one UDC), bypassing the two-UDC chain int → Z → Rational<Z> that C++ would otherwise reject. This Rational(Z) form remains the canonical embedding when the caller already has a Z value in hand.
template<std::integral S>
requires (!std::same_as<S, Z>)
constexpr Rational (S n)
 Embedding of any standard integral as a rational n/1, in one user-defined conversion.
constexpr void reject_non_finite () const
 #680: ℚ has no sentinels. On the saturating SignedCardinality a non-finite numerator/denominator (NaZ / ±ℵ_0) is not a rational, so reject it with std::domain_error (Honest Rejection at the carrier boundary, the same posture as division-by-zero, cf. the embed_𝕃_ℚ no-sentinels contract below). This also avoids euclidean_gcd's non-termination (NaZ % anything == NaZ) and the ordering incoherence a retained NaZ/1 would cause (<=> would read it EQUAL to 1/1 while == reads it unequal). Generic over the backing through RejectsNonFinite (the is_finite hook); a compile-time no-op for carriers without sentinels (plain integers).
constexpr void simplify ()
constexpr Z num () const
constexpr Z den () const
constexpr Rational inverse () const
constexpr Rational operator- () const

Static Public Member Functions

static constexpr void reject_non_finite_product (const Z &lhs, const Z &rhs)
 #680: the comparison-side OOM tripwire. Throws std::domain_error when a cross-product a.first*b.second (or its twin) has saturated to a non-finite value, i.e. the comparison has left the finite backing. Compile-time no-op for carriers with no sentinels.

Public Attributes

Z first
Z second

Friends

constexpr std::strong_ordering operator<=> (const Rational &a, const Rational &b)
constexpr bool operator== (const Rational &, const Rational &)=default
constexpr Rational operator* (const Rational &a, const Rational &b)
constexpr Rational operator+ (const Rational &a, const Rational &b)
constexpr Rational operator- (const Rational &a, const Rational &b)
constexpr Rational operator/ (const Rational &a, const Rational &b)

Detailed Description

template<IsInteger Z = default_integer>
class dedekind::numbers::Rational< Z >

The Field of Fractions over an Integral Domain Z.

The countably-infinite fiction. Over the saturating carrier SignedCardinality this type models ℚ as a faithful, strong-totally-ordered field over a countably infinite ℤ — even though no finite machine can hold arbitrarily large numerators / denominators. The carrier's sentinels (NaZ, ±ℵ_0) are not inhabitants of that ℚ; they are the out-of-memory boundary of the finite backing. Reaching one means the computation has run off the edge of the fiction, so a non-finite numerator / denominator is rejected (std::domain_error, see reject_non_finite) rather than admitted as a value — the OOM tripwire. This is what keeps operator<=> a genuine std::strong_ordering and preserves IsTotal / IsField (both load-bearing for the ℝ coat-hanger downstream); a retained NaZ/1 would force <=> to partial_ordering and quietly demote ℚ to a non-total order.

Comparison is a tripwire too. operator<=> cross-multiplies ( \(a/b \lessgtr c/d \iff ad \lessgtr cb\)), and on the saturating carrier the cross-products can overflow even for two finite canonical rationals (e.g. \(M/2\) vs \((M{-}1)/3\) near UINT64_MAX). Rather than let a saturated intermediate read them EQUAL while == reads them unequal, <=> rejects an overflowed cross-product (reject_non_finite_product) — the same OOM tripwire. So the strong-total-order guarantee is precise: every <=> that returns is a genuine strong-total verdict; a comparison that would leave the finite backing throws instead. Over the client-contracted finite subset the branch is never taken.

Client contract.
The fiction is deliberate: client code may pretend ℚ is truly infinite, at the risk of hitting the OOM tripwire (in construction or comparison). Robust client code constrains itself to a finite subset a priori — e.g. integers \(-\text{max} < x < \text{max}\), or the canonicalised rationals over that range — so the tripwire is never reached. The library does not silently clamp on the client's behalf; it throws, so the boundary stays visible (Honest Rejection, the same posture as division-by-zero).

Constructor & Destructor Documentation

◆ Rational()

template<IsInteger Z = default_integer>
template<std::integral S>
requires (!std::same_as<S, Z>)
dedekind::numbers::Rational< Z >::Rational ( S n)
inlineconstexpr

Embedding of any standard integral as a rational n/1, in one user-defined conversion.

Without this constructor, building a Rational<Z> from an int literal would require two UDCs (int → Z → Rational<Z>), which C++ does not permit in implicit-conversion chains. This template collapses both steps into a single UDC, enabling:

  • Rational<Z>{2} for the (a + b) / T{2} midpoint expression in the IsDense concept (the T{2} reformulation, landed under this PR, replaces the previous bare-int 2 so the carrier is in control of its own value-2);
  • generally, any context where an int literal lifts into Rational<Z> across one UDC.

Canonical example post-retarget: Z = SignedExtensionalCardinal<>, whose S → SEC<> constructor is itself a one-UDC step.

Member Function Documentation

◆ inverse()

template<IsInteger Z = default_integer>
Rational dedekind::numbers::Rational< Z >::inverse ( ) const
inlineconstexpr

◆ simplify()

template<IsInteger Z = default_integer>
void dedekind::numbers::Rational< Z >::simplify ( )
inlineconstexpr

rational__The_Simplification_Morphism

◆ operator*

template<IsInteger Z = default_integer>
Rational operator* ( const Rational< Z > & a,
const Rational< Z > & b )
friend

rational__Field_Operators

◆ operator<=>

template<IsInteger Z = default_integer>
std::strong_ordering operator<=> ( const Rational< Z > & a,
const Rational< Z > & b )
friend

rational__Relational_Morphisms


The documentation for this class was generated from the following file: