865 lines
30 KiB
Swift
865 lines
30 KiB
Swift
//
|
|
// Rational.swift
|
|
// NumericAnnex
|
|
//
|
|
// Created by Xiaodi Wu on 4/15/17.
|
|
//
|
|
|
|
/// A type to represent a rational value.
|
|
///
|
|
/// - Note: `Ratio` is a type alias for `Rational<Int>`.
|
|
///
|
|
/// Create new instances of `Rational<T>` by using integer literals and the
|
|
/// division (`/`) operator. For example:
|
|
///
|
|
/// ```swift
|
|
/// let x = 1 / 3 as Ratio // `x` is of type `Rational<Int>`
|
|
/// let y = 2 as Ratio // `y` is of type `Rational<Int>`
|
|
/// let z: Ratio = 2 / 3 // `z` is also of type `Rational<Int>`
|
|
///
|
|
/// print(x + y + z) // Prints "3"
|
|
/// ```
|
|
///
|
|
/// You can create an unreduced fraction by using the initializer
|
|
/// `Rational<T>.init(numerator:denominator:)`. For example:
|
|
///
|
|
/// ```swift
|
|
/// let a = Ratio(numerator: 3, denominator: 3)
|
|
/// print(a) // Prints "3/3"
|
|
/// ```
|
|
///
|
|
/// All arithmetic operations on values in canonical form (i.e. reduced to
|
|
/// lowest terms) return results in canonical form. However, operations on
|
|
/// values not in canonical form may or may not return results that are
|
|
/// themselves in canonical form. The property `canonicalized` is the canonical
|
|
/// form of any value.
|
|
///
|
|
/// Additional Considerations
|
|
/// -------------------------
|
|
///
|
|
/// ### Special Values
|
|
///
|
|
/// `Rational<T>` does not prohibit zero as a denominator. Any instance with a
|
|
/// positive numerator and zero denominator represents (positive) infinity; any
|
|
/// instance with a negative numerator and zero denominator represents negative
|
|
/// infinity; and any instance with zero numerator and zero denominator
|
|
/// represents NaN ("not a number").
|
|
///
|
|
/// As with floating-point types, `Rational<T>.infinity` compares greater than
|
|
/// every finite value and negative infinity, and `-Rational<T>.infinity`
|
|
/// compares less than every finite value and positive infinity. Infinite values
|
|
/// of the same sign compare equal to each other.
|
|
///
|
|
/// As with floating-point types, `Rational<T>.nan` does not compare equal to
|
|
/// any other value, including another NaN. Use the property `isNaN` to test if
|
|
/// a value is NaN. `Rational<T>` arithmetic operations are intended to
|
|
/// propagate NaN in the same manner as analogous floating-point operations.
|
|
///
|
|
/// ### Numerical Limits
|
|
///
|
|
/// When a value of type `Rational<T>` is in canonical form, the sign of the
|
|
/// numerator is the sign of the value; that is, in canonical form, the sign of
|
|
/// the denominator is always positive. Therefore, `-1 / T.min` cannot be
|
|
/// represented as a value of type `Rational<T>` because `abs(T.min)` cannot be
|
|
/// represented as a value of type `T`.
|
|
///
|
|
/// To ensure that every representable value of type `Rational<T>` has a
|
|
/// representable magnitude and reciprocal of the same type, an overflow trap
|
|
/// occurs when the division (`/`) operator is used to create a value of type
|
|
/// `Rational<T>` with numerator `T.min`.
|
|
@_fixed_layout
|
|
public struct Rational<T : SignedInteger> : Codable
|
|
where T : Codable & _ExpressibleByBuiltinIntegerLiteral,
|
|
T.Magnitude : UnsignedInteger,
|
|
T.Magnitude.Magnitude == T.Magnitude {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Stored Properties
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// The numerator of the rational value.
|
|
public var numerator: T
|
|
|
|
/// The denominator of the rational value.
|
|
public var denominator: T
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Initializers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Creates a new value from the given numerator and denominator without
|
|
/// computing its canonical form (i.e., without reducing to lowest terms).
|
|
///
|
|
/// To create a value reduced to lowest terms, use the division (`/`)
|
|
/// operator. For example:
|
|
///
|
|
/// ```swift
|
|
/// let x = 3 / 3 as Rational<Int>
|
|
/// print(x) // Prints "1"
|
|
/// ```
|
|
///
|
|
/// - Parameters:
|
|
/// - numerator: The new value's numerator.
|
|
/// - denominator: The new value's denominator.
|
|
@_transparent // @_inlineable
|
|
public init(numerator: T, denominator: T) {
|
|
self.numerator = numerator
|
|
self.denominator = denominator
|
|
}
|
|
|
|
/// Creates a new rational value from the given binary integer.
|
|
///
|
|
/// If `source` or its magnitude is not representable as a numerator of type
|
|
/// `T`, a runtime error may occur.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: A binary integer to convert to a rational value.
|
|
@_transparent // @_inlineable
|
|
public init<Source : BinaryInteger>(_ source: Source) {
|
|
let t = T(source)
|
|
// Ensure that `t.magnitude` is representable as a `T`.
|
|
_ = T(t.magnitude)
|
|
self.numerator = t
|
|
self.denominator = 1
|
|
}
|
|
|
|
/// Creates a new rational value from the given binary floating-point value.
|
|
///
|
|
/// If `source` or its magnitude is not representable exactly as a ratio of
|
|
/// two signed integers of type `T`, a runtime error may occur.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: A binary floating-point value to convert to a rational value.
|
|
@_transparent // @_inlineable
|
|
public init<Source : BinaryFloatingPoint>(_ source: Source) {
|
|
if source.isNaN { self = .nan; return }
|
|
if source == .infinity { self = .infinity; return }
|
|
if source == -.infinity { self = -.infinity; return }
|
|
if source.isZero { self = 0; return }
|
|
|
|
let exponent = source.exponent
|
|
let significandWidth = source.significandWidth
|
|
let shift = Source.Exponent(significandWidth) - exponent
|
|
|
|
if shift <= 0 {
|
|
self.numerator = T(source)
|
|
self.denominator = 1
|
|
return
|
|
}
|
|
|
|
let numerator = T(
|
|
Source(
|
|
sign: source.sign,
|
|
exponent: exponent + shift,
|
|
significand: source.significand
|
|
)
|
|
)
|
|
let denominator = T(Source(sign: .plus, exponent: shift, significand: 1))
|
|
// Ensure that `numerator.magnitude` and `denominator.magnitude` are each
|
|
// representable as a `T`.
|
|
_ = T(numerator.magnitude)
|
|
_ = T(denominator.magnitude)
|
|
self.numerator = numerator
|
|
self.denominator = denominator
|
|
}
|
|
}
|
|
|
|
extension Rational where T : FixedWidthInteger, T.Magnitude : FixedWidthInteger {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Initializers (Constrained)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Creates a new rational value from the given binary floating-point value,
|
|
/// if it can be represented exactly.
|
|
///
|
|
/// If `source` or its magnitude is not representable exactly as a ratio of
|
|
/// two signed integers of type `T`, the result is `nil`.
|
|
///
|
|
/// - Note: This initializer creates only instances of
|
|
/// `Rational<T> where T : FixedWidthInteger`.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: A floating-point value to convert to a rational value.
|
|
@_transparent // @_inlineable
|
|
public init?<Source : BinaryFloatingPoint>(exactly source: Source) {
|
|
// TODO: Document this initializer.
|
|
if source.isNaN { self = .nan; return }
|
|
if source == .infinity { self = .infinity; return }
|
|
if source == -.infinity { self = -.infinity; return }
|
|
if source.isZero { self = 0; return } // Consider -0.0 to be exactly 0.
|
|
|
|
let exponent = source.exponent
|
|
let significandWidth = source.significandWidth
|
|
let shift = Source.Exponent(significandWidth) - exponent
|
|
let bitWidth = T.bitWidth
|
|
|
|
if shift <= 0 {
|
|
guard exponent + 1 < bitWidth else { return nil }
|
|
self.numerator = T(source)
|
|
self.denominator = 1
|
|
return
|
|
}
|
|
|
|
guard significandWidth + 1 < bitWidth && shift + 1 < bitWidth else {
|
|
return nil
|
|
}
|
|
self.numerator = T(
|
|
Source(
|
|
sign: source.sign,
|
|
exponent: exponent + shift,
|
|
significand: source.significand
|
|
)
|
|
)
|
|
self.denominator = T(Source(sign: .plus, exponent: shift, significand: 1))
|
|
}
|
|
}
|
|
|
|
extension Rational {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Static Properties
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Positive infinity.
|
|
///
|
|
/// Infinity compares greater than all finite numbers and equal to other
|
|
/// (positive) infinite values.
|
|
@_transparent // @_inlineable
|
|
public static var infinity: Rational {
|
|
return Rational(numerator: 1, denominator: 0)
|
|
}
|
|
|
|
/// A quiet NaN ("not a number").
|
|
///
|
|
/// A NaN compares not equal, not greater than, and not less than every value,
|
|
/// including itself. Passing a NaN to an operation generally results in NaN.
|
|
@_transparent // @_inlineable
|
|
public static var nan: Rational {
|
|
return Rational(numerator: 0, denominator: 0)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Static Methods
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Compares the (finite) magnitude of two finite values, returning -1 if
|
|
/// `lhs.magnitude` is less than `rhs.magnitude`, 0 if `lhs.magnitude` is
|
|
/// equal to `rhs.magnitude`, or 1 if `lhs.magnitude` is greater than
|
|
/// `rhs.magnitude`.
|
|
@_versioned
|
|
internal static func _compareFiniteMagnitude(
|
|
_ lhs: Rational, _ rhs: Rational
|
|
) -> Int {
|
|
let ldm = lhs.denominator.magnitude
|
|
let rdm = rhs.denominator.magnitude
|
|
let gcd = T.Magnitude.gcd(ldm, rdm)
|
|
let a = rdm / gcd * lhs.numerator.magnitude
|
|
let b = ldm / gcd * rhs.numerator.magnitude
|
|
return a == b ? 0 : (a < b ? -1 : 1)
|
|
// FIXME: Use full-width multiplication to avoid trapping on overflow
|
|
// where `T : FixedWidthInteger, T.Magnitude : FixedWidthInteger`.
|
|
/*
|
|
let a = (rdm / gcd).multipliedFullWidth(by: lhs.numerator.magnitude)
|
|
let b = (ldm / gcd).multipliedFullWidth(by: rhs.numerator.magnitude)
|
|
return a.high == b.high
|
|
? (a.low == b.low ? 0 : (a.low < b.low ? -1 : 1))
|
|
: (a.high < b.high ? -1 : 1)
|
|
*/
|
|
}
|
|
|
|
/// Returns the quotient obtained by dividing the first value by the second,
|
|
/// trapping in case of arithmetic overflow.
|
|
///
|
|
/// - Parameters:
|
|
/// - lhs: The value to divide.
|
|
/// - rhs: The value by which to divide `lhs`.
|
|
@_transparent // @_inlineable
|
|
public static func / (lhs: Rational, rhs: Rational) -> Rational {
|
|
return lhs * rhs.reciprocal()
|
|
}
|
|
|
|
/// Divides the left-hand side by the right-hand side and stores the quotient
|
|
/// in the left-hand side, trapping in case of arithmetic overflow.
|
|
///
|
|
/// - Parameters:
|
|
/// - lhs: The value to divide.
|
|
/// - rhs: The value by which to divide `lhs`.
|
|
@_transparent // @_inlineable
|
|
public static func /= (lhs: inout Rational, rhs: Rational) {
|
|
lhs = lhs * rhs.reciprocal()
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Computed Properties
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// The canonical representation of this value.
|
|
@_transparent // @_inlineable
|
|
public var canonical: Rational {
|
|
let nm = numerator.magnitude, dm = denominator.magnitude
|
|
// Note that if `T` is a signed fixed-width integer type, `gcd(nm, dm)`
|
|
// could be equal to `-T.min`, which is not representable as a `T`. This is
|
|
// why the following arithmetic is performed with values of type
|
|
// `T.Magnitude`.
|
|
let gcd = T.Magnitude.gcd(nm, dm)
|
|
guard gcd != 0 else { return self }
|
|
let n = sign == .plus ? T(nm / gcd) : -T(nm / gcd)
|
|
let d = T(dm / gcd)
|
|
return Rational(numerator: n, denominator: d)
|
|
}
|
|
|
|
/// A Boolean value indicating whether the instance's representation is in
|
|
/// canonical form.
|
|
@_transparent // @_inlineable
|
|
public var isCanonical: Bool {
|
|
if denominator > 0 {
|
|
return T.Magnitude.gcd(numerator.magnitude, denominator.magnitude) == 1
|
|
}
|
|
return denominator == 0 && numerator.magnitude <= 1
|
|
}
|
|
|
|
/// A Boolean value indicating whether the instance is finite.
|
|
///
|
|
/// All values other than NaN and infinity are considered finite.
|
|
@_transparent // @_inlineable
|
|
public var isFinite: Bool {
|
|
return denominator != 0
|
|
}
|
|
|
|
/// A Boolean value indicating whether the instance is infinite.
|
|
///
|
|
/// Note that `isFinite` and `isInfinite` do not form a dichotomy because NaN
|
|
/// is neither finite nor infinite.
|
|
@_transparent // @_inlineable
|
|
public var isInfinite: Bool {
|
|
return denominator == 0 && numerator != 0
|
|
}
|
|
|
|
/// A Boolean value indicating whether the instance is NaN ("not a number").
|
|
///
|
|
/// Because NaN is not equal to any value, including NaN, use this property
|
|
/// instead of the equal-to operator (`==`) or not-equal-to operator (`!=`) to
|
|
/// test whether a value is or is not NaN.
|
|
@_transparent // @_inlineable
|
|
public var isNaN: Bool {
|
|
return denominator == 0 && numerator == 0
|
|
}
|
|
|
|
/// A Boolean value indicating whether the instance is a proper fraction.
|
|
///
|
|
/// A fraction is proper if and only if the absolute value of the fraction is
|
|
/// less than 1.
|
|
@_transparent // @_inlineable
|
|
public var isProper: Bool {
|
|
return denominator != 0 && numerator / denominator == 0
|
|
}
|
|
|
|
/// A Boolean value indicating whether the instance is equal to zero.
|
|
@_transparent // @_inlineable
|
|
public var isZero: Bool {
|
|
return denominator != 0 && numerator == 0
|
|
}
|
|
|
|
/// The magnitude (absolute value) of this value.
|
|
@_transparent // @_inlineable
|
|
public var magnitude: Rational {
|
|
return sign == .minus ? -self : self
|
|
}
|
|
|
|
/// The mixed form representing this value.
|
|
///
|
|
/// If the value is not finite, the mixed form has zero as its whole part and
|
|
/// the value as its fractional part.
|
|
@_transparent // @_inlineable
|
|
public var mixed: (whole: T, fractional: Rational) {
|
|
if denominator == 0 { return (whole: 0, fractional: self) }
|
|
let t = numerator.quotientAndRemainder(dividingBy: denominator)
|
|
return (
|
|
whole: t.quotient,
|
|
fractional: Rational(numerator: t.remainder, denominator: denominator)
|
|
)
|
|
}
|
|
|
|
/// The sign of this value.
|
|
@_transparent // @_inlineable
|
|
public var sign: Sign {
|
|
return numerator == 0 || (denominator < 0) == (numerator < 0)
|
|
? .plus
|
|
: .minus
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Methods
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Returns the reciprocal (multiplicative inverse) of this value.
|
|
@_transparent // @_inlineable
|
|
public func reciprocal() -> Rational {
|
|
return numerator < 0
|
|
? Rational(numerator: -denominator, denominator: -numerator)
|
|
: Rational(numerator: denominator, denominator: numerator)
|
|
}
|
|
|
|
/// Returns this value rounded to an integral value using the specified
|
|
/// rounding rule.
|
|
///
|
|
/// ```swift
|
|
/// let x = 7 / 2 as Rational<Int>
|
|
/// print(x.rounded()) // Prints "4"
|
|
/// print(x.rounded(.towardZero)) // Prints "3"
|
|
/// print(x.rounded(.up)) // Prints "4"
|
|
/// print(x.rounded(.down)) // Prints "3"
|
|
/// ```
|
|
///
|
|
/// See the `FloatingPointRoundingRule` enumeration for more information about
|
|
/// the available rounding rules.
|
|
///
|
|
/// - Parameters:
|
|
/// - rule: The rounding rule to use.
|
|
///
|
|
/// - SeeAlso: `round(_:)`, `RoundingRule`
|
|
@_transparent // @_inlineable
|
|
public func rounded(
|
|
_ rule: RoundingRule = .toNearestOrAwayFromZero
|
|
) -> Rational {
|
|
var t = self
|
|
t.round(rule)
|
|
return t
|
|
}
|
|
|
|
/// Rounds the value to an integral value using the specified rounding rule.
|
|
///
|
|
/// ```swift
|
|
/// var x = 7 / 2 as Rational<Int>
|
|
/// x.round() // x == 4
|
|
///
|
|
/// var x = 7 / 2 as Rational<Int>
|
|
/// x.round(.towardZero) // x == 3
|
|
///
|
|
/// var x = 7 / 2 as Rational<Int>
|
|
/// x.round(.up) // x == 4
|
|
///
|
|
/// var x = 7 / 2 as Rational<Int>
|
|
/// x.round(.down) // x == 3
|
|
/// ```
|
|
///
|
|
/// See the `FloatingPointRoundingRule` enumeration for more information about
|
|
/// the available rounding rules.
|
|
///
|
|
/// - Parameters:
|
|
/// - rule: The rounding rule to use.
|
|
///
|
|
/// - SeeAlso: `round(_:)`, `RoundingRule`
|
|
@_transparent // @_inlineable
|
|
public mutating func round(_ rule: RoundingRule = .toNearestOrAwayFromZero) {
|
|
if denominator == 0 { return }
|
|
|
|
let f: T
|
|
(numerator, f) = numerator.quotientAndRemainder(dividingBy: denominator)
|
|
// Rounding rules only come into play if the fractional part is non-zero.
|
|
if f != 0 {
|
|
switch rule {
|
|
case .toNearestOrAwayFromZero:
|
|
fallthrough
|
|
case .toNearestOrEven:
|
|
switch denominator.magnitude.quotientAndRemainder(
|
|
dividingBy: f.magnitude
|
|
) {
|
|
case (2, 0): // Tie.
|
|
if rule == .toNearestOrEven && numerator % 2 == 0 { break }
|
|
fallthrough
|
|
case (1, _): // Nearest is away from zero.
|
|
if f > 0 { numerator += 1 } else { numerator -= 1 }
|
|
default: // Nearest is toward zero.
|
|
break
|
|
}
|
|
case .up:
|
|
if f > 0 { numerator += 1 }
|
|
case .down:
|
|
if f < 0 { numerator -= 1 }
|
|
case .towardZero:
|
|
break
|
|
case .awayFromZero:
|
|
if f > 0 { numerator += 1 } else { numerator -= 1 }
|
|
}
|
|
}
|
|
denominator = 1
|
|
}
|
|
}
|
|
|
|
extension Rational : ExpressibleByIntegerLiteral {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: ExpressibleByIntegerLiteral
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@_transparent // @_inlineable
|
|
public init(integerLiteral value: T) {
|
|
self.numerator = value
|
|
self.denominator = 1
|
|
}
|
|
}
|
|
|
|
extension Rational : CustomStringConvertible {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: CustomStringConvertible
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@_transparent // @_inlineable
|
|
public var description: String {
|
|
if numerator == 0 { return denominator == 0 ? "nan" : "0" }
|
|
if denominator == 0 { return numerator < 0 ? "-inf" : "inf" }
|
|
return denominator == 1 ? "\(numerator)" : "\(numerator)/\(denominator)"
|
|
}
|
|
}
|
|
|
|
extension Rational : Equatable {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Equatable
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@_transparent // @_inlineable
|
|
public static func == (lhs: Rational, rhs: Rational) -> Bool {
|
|
if lhs.denominator == 0 {
|
|
if lhs.numerator == 0 { return false }
|
|
if lhs.numerator > 0 { return rhs.denominator == 0 && rhs.numerator > 0 }
|
|
return rhs.denominator == 0 && rhs.numerator < 0
|
|
}
|
|
if rhs.denominator == 0 { return false }
|
|
|
|
return lhs.sign == rhs.sign && _compareFiniteMagnitude(lhs, rhs) == 0
|
|
}
|
|
}
|
|
|
|
extension Rational : Hashable {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Hashable
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// @_transparent // @_inlineable
|
|
public var hashValue: Int {
|
|
let t = canonical
|
|
return _Hash._combine(t.numerator, t.denominator)
|
|
}
|
|
}
|
|
|
|
extension Rational : Comparable {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Comparable
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@_transparent // @_inlineable
|
|
public static func < (lhs: Rational, rhs: Rational) -> Bool {
|
|
if lhs.denominator == 0 {
|
|
if lhs.numerator >= 0 { return false }
|
|
return rhs.denominator != 0 || rhs.numerator > 0
|
|
}
|
|
if rhs.denominator == 0 { return rhs.numerator > 0 }
|
|
|
|
switch (lhs.sign, rhs.sign) {
|
|
case (.plus, .minus):
|
|
return false
|
|
case (.minus, .plus):
|
|
return true
|
|
case (.plus, .plus):
|
|
return _compareFiniteMagnitude(lhs, rhs) < 0
|
|
case (.minus, .minus):
|
|
return _compareFiniteMagnitude(lhs, rhs) > 0
|
|
}
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func > (lhs: Rational, rhs: Rational) -> Bool {
|
|
return rhs < lhs
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func <= (lhs: Rational, rhs: Rational) -> Bool {
|
|
if lhs.denominator == 0 {
|
|
if lhs.numerator == 0 { return false }
|
|
if lhs.numerator > 0 { return rhs.denominator == 0 && rhs.numerator > 0 }
|
|
return rhs.denominator != 0 || rhs.numerator != 0
|
|
}
|
|
if rhs.denominator == 0 { return rhs.numerator > 0 }
|
|
|
|
switch (lhs.sign, rhs.sign) {
|
|
case (.plus, .minus):
|
|
return false
|
|
case (.minus, .plus):
|
|
return true
|
|
case (.plus, .plus):
|
|
return _compareFiniteMagnitude(lhs, rhs) <= 0
|
|
case (.minus, .minus):
|
|
return _compareFiniteMagnitude(lhs, rhs) >= 0
|
|
}
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func >= (lhs: Rational, rhs: Rational) -> Bool {
|
|
return rhs <= lhs
|
|
}
|
|
}
|
|
|
|
extension Rational : Strideable {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Strideable
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@_transparent // @_inlineable
|
|
public func distance(to other: Rational) -> Rational {
|
|
return other - self
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public func advanced(by amount: Rational) -> Rational {
|
|
return self + amount
|
|
}
|
|
}
|
|
|
|
extension Rational : Numeric {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Numeric
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@_transparent // @_inlineable
|
|
public init?<U>(exactly source: U) where U : BinaryInteger {
|
|
guard let t = T(exactly: source) else { return nil }
|
|
// Ensure that `t.magnitude` is representable as a `T`.
|
|
guard let _ = T(exactly: t.magnitude) else { return nil }
|
|
self.numerator = t
|
|
self.denominator = 1
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func + (lhs: Rational, rhs: Rational) -> Rational {
|
|
if lhs.denominator == 0 {
|
|
if rhs.denominator != 0 || lhs.numerator == 0 { return lhs }
|
|
if lhs.numerator > 0 { return rhs.numerator < 0 ? .nan : rhs }
|
|
return rhs.numerator > 0 ? .nan : rhs
|
|
}
|
|
if rhs.denominator == 0 { return rhs }
|
|
|
|
let ldm = lhs.denominator.magnitude
|
|
let rdm = rhs.denominator.magnitude
|
|
let gcd = T.Magnitude.gcd(ldm, rdm)
|
|
let a = T(rdm / gcd * lhs.numerator.magnitude)
|
|
let b = T(ldm / gcd * rhs.numerator.magnitude)
|
|
let n = lhs.sign == .plus
|
|
? (rhs.sign == .plus ? a + b : a - b)
|
|
: (rhs.sign == .plus ? b - a : -a - b)
|
|
let d = T(ldm / gcd * rdm)
|
|
return Rational(numerator: n, denominator: d).canonical
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func += (lhs: inout Rational, rhs: Rational) {
|
|
lhs = lhs + rhs
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func - (lhs: Rational, rhs: Rational) -> Rational {
|
|
return lhs + (-rhs)
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func -= (lhs: inout Rational, rhs: Rational) {
|
|
lhs = lhs + (-rhs)
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func * (lhs: Rational, rhs: Rational) -> Rational {
|
|
if lhs.denominator == 0 {
|
|
if rhs.numerator == 0 { return .nan }
|
|
return rhs.sign == .plus ? lhs : -lhs
|
|
}
|
|
if rhs.denominator == 0 {
|
|
if lhs.numerator == 0 { return .nan }
|
|
return lhs.sign == .plus ? rhs : -rhs
|
|
}
|
|
|
|
let lnm = lhs.numerator.magnitude, ldm = lhs.denominator.magnitude
|
|
let rnm = rhs.numerator.magnitude, rdm = rhs.denominator.magnitude
|
|
// Note that if `T` is a signed fixed-width integer type, `gcd(lnm, rdm)` or
|
|
// `gcd(rnm, ldm)` could be equal to `-T.min`, which is not representable as
|
|
// a `T`. This is why the following arithmetic is performed with values of
|
|
// type `T.Magnitude`.
|
|
let a = T.Magnitude.gcd(lnm, rdm)
|
|
let b = T.Magnitude.gcd(rnm, ldm)
|
|
let n = lhs.sign == rhs.sign
|
|
? T(lnm / a * (rnm / b))
|
|
: -T(lnm / a * (rnm / b))
|
|
let d = T(ldm / b * (rdm / a))
|
|
return Rational(numerator: n, denominator: d)
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public static func *= (lhs: inout Rational, rhs: Rational) {
|
|
lhs = lhs * rhs
|
|
}
|
|
}
|
|
|
|
extension Rational : SignedNumeric {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: SignedNumeric
|
|
// ---------------------------------------------------------------------------
|
|
|
|
@_transparent // @_inlineable
|
|
public static prefix func - (operand: Rational) -> Rational {
|
|
return Rational(
|
|
numerator: -operand.numerator, denominator: operand.denominator
|
|
)
|
|
}
|
|
|
|
@_transparent // @_inlineable
|
|
public mutating func negate() {
|
|
numerator.negate()
|
|
}
|
|
}
|
|
|
|
/// Returns the absolute value (magnitude) of `x`.
|
|
@_transparent
|
|
public func abs<T>(_ x: Rational<T>) -> Rational<T> {
|
|
return x.magnitude
|
|
}
|
|
|
|
/// Returns the closest integral value greater than or equal to `x`.
|
|
@_transparent
|
|
public func ceil<T>(_ x: Rational<T>) -> Rational<T> {
|
|
return x.rounded(.up)
|
|
}
|
|
|
|
/// Returns the closest integral value less than or equal to `x`.
|
|
@_transparent
|
|
public func floor<T>(_ x: Rational<T>) -> Rational<T> {
|
|
return x.rounded(.down)
|
|
}
|
|
|
|
/// Returns the closest integral value; if two values are equally close, returns
|
|
/// the one with greater magnitude.
|
|
@_transparent
|
|
public func round<T>(_ x: Rational<T>) -> Rational<T> {
|
|
return x.rounded()
|
|
}
|
|
|
|
/// Returns the closest integral value with magnitude less than or equal to that
|
|
/// of `x`.
|
|
@_transparent
|
|
public func trunc<T>(_ x: Rational<T>) -> Rational<T> {
|
|
return x.rounded(.towardZero)
|
|
}
|
|
|
|
public typealias Ratio = Rational<Int>
|
|
|
|
// MARK: -
|
|
|
|
extension BinaryInteger {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Initializers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Creates a new binary integer from the given rational value, if it can be
|
|
/// represented exactly.
|
|
///
|
|
/// If `source` is not representable exactly, the result is `nil`.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: A rational value to convert to a binary integer.
|
|
@_transparent // @_inlineable
|
|
public init?<U>(exactly source: Rational<U>) {
|
|
let (whole, fraction) = source.mixed
|
|
guard fraction.isZero, let exact = Self(exactly: whole) else { return nil }
|
|
self = exact
|
|
}
|
|
|
|
/// Creates a new binary integer from the given rational value, rounding
|
|
/// toward zero.
|
|
///
|
|
/// If `source` is outside the bounds of this type after rounding toward zero,
|
|
/// a runtime error may occur.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: A rational value to convert to a binary integer.
|
|
@_transparent // @_inlineable
|
|
public init<U>(_ source: Rational<U>) {
|
|
self = Self(source.mixed.whole)
|
|
}
|
|
}
|
|
|
|
extension FloatingPoint {
|
|
// ---------------------------------------------------------------------------
|
|
// MARK: Initializers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Creates a new floating-point value from the given rational value, after
|
|
/// rounding the whole part, the numerator of the fractional part, and the
|
|
/// denominator of the fractional part each to the closest possible
|
|
/// representation.
|
|
///
|
|
/// If two representable values are equally close, the result of rounding is
|
|
/// the value with more trailing zeros in its significand bit pattern.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: The rational value to convert to a floating-point value.
|
|
public init(_ source: Rational<Int>) {
|
|
let (whole, fraction) = source.mixed
|
|
self = Self(whole) + Self(fraction.numerator) / Self(fraction.denominator)
|
|
}
|
|
|
|
/// Creates a new floating-point value from the given rational value, after
|
|
/// rounding the whole part, the numerator of the fractional part, and the
|
|
/// denominator of the fractional part each to the closest possible
|
|
/// representation.
|
|
///
|
|
/// If two representable values are equally close, the result of rounding is
|
|
/// the value with more trailing zeros in its significand bit pattern.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: The rational value to convert to a floating-point value.
|
|
public init(_ source: Rational<Int8>) {
|
|
let (whole, fraction) = source.mixed
|
|
self = Self(whole) + Self(fraction.numerator) / Self(fraction.denominator)
|
|
}
|
|
|
|
/// Creates a new floating-point value from the given rational value, after
|
|
/// rounding the whole part, the numerator of the fractional part, and the
|
|
/// denominator of the fractional part each to the closest possible
|
|
/// representation.
|
|
///
|
|
/// If two representable values are equally close, the result of rounding is
|
|
/// the value with more trailing zeros in its significand bit pattern.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: The rational value to convert to a floating-point value.
|
|
public init(_ source: Rational<Int16>) {
|
|
let (whole, fraction) = source.mixed
|
|
self = Self(whole) + Self(fraction.numerator) / Self(fraction.denominator)
|
|
}
|
|
|
|
/// Creates a new floating-point value from the given rational value, after
|
|
/// rounding the whole part, the numerator of the fractional part, and the
|
|
/// denominator of the fractional part each to the closest possible
|
|
/// representation.
|
|
///
|
|
/// If two representable values are equally close, the result of rounding is
|
|
/// the value with more trailing zeros in its significand bit pattern.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: The rational value to convert to a floating-point value.
|
|
public init(_ source: Rational<Int32>) {
|
|
let (whole, fraction) = source.mixed
|
|
self = Self(whole) + Self(fraction.numerator) / Self(fraction.denominator)
|
|
}
|
|
|
|
/// Creates a new floating-point value from the given rational value, after
|
|
/// rounding the whole part, the numerator of the fractional part, and the
|
|
/// denominator of the fractional part each to the closest possible
|
|
/// representation.
|
|
///
|
|
/// If two representable values are equally close, the result of rounding is
|
|
/// the value with more trailing zeros in its significand bit pattern.
|
|
///
|
|
/// - Parameters:
|
|
/// - source: The rational value to convert to a floating-point value.
|
|
public init(_ source: Rational<Int64>) {
|
|
let (whole, fraction) = source.mixed
|
|
self = Self(whole) + Self(fraction.numerator) / Self(fraction.denominator)
|
|
}
|
|
}
|