Skip to content

Commit

Permalink
Simplified Swift protocol compliance in a single file (with minimal r…
Browse files Browse the repository at this point in the history
…enaming).
  • Loading branch information
mgriebling committed Jul 14, 2023
1 parent 1e0fb2f commit a70db7b
Show file tree
Hide file tree
Showing 15 changed files with 302 additions and 145 deletions.
7 changes: 4 additions & 3 deletions Sources/BigInt/BigFrac.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@

/// A signed fraction with numerator and denominator of unbounded size.
/// A BFraction value is represented by a BInt numerator and a BInt denominator.
/// The representation is normalized, so that numerator and denominator has no common divisors except 1.
/// The denominator is always positive, 0 has the representation 0/1
/// The representation is normalized, so that numerator and denominator has no
/// common divisors except 1. The denominator is always positive, 0 has the
/// representation 0/1.
public struct BFraction: CustomStringConvertible, Comparable, Equatable {

mutating func normalize() {
let g = self.numerator.gcd(self.denominator)
if g.magnitude.compare(1) > 0 {
if g.words.compare(1) > 0 {
self.numerator = self.numerator.quotientExact(dividingBy: g)
self.denominator = self.denominator.quotientExact(dividingBy: g)
}
Expand Down
126 changes: 126 additions & 0 deletions Sources/BigInt/BigInt-Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//
// BigInt-Extensions.swift
//
// Created by Mike Griebling on 13.07.2023.
//
/// Added `SignedInteger`, `BinaryInteger`, and `Numeric` protocol compliance.
/// Optional support for `StaticBigInt`. Note: These extensions require
/// renaming `magnitude` to `words` to avoid conflict with the
/// `Numeric` protocol variable also called `magnitude`.
///
/// Why support protocols? By supporting them you have the ability to
/// formulate generic algorithms and make use of algorithms from others
/// that use the protocol type(s) you support. For example, `Strideable`
/// compliance is free (with `BinaryInteger`) and lets you do things like
///
/// ```swift
/// for i in BInt(1)...10 {
/// print(i.words)
/// }
/// ```
///
/// The main header also
/// includes `Codable` compliance conformity (for free). `Codable`
/// compliance allows `BInt`s to be distributed/received or stored/read as
/// JSONs.
///
/// Protocols mean you can support generic arguments:
/// (e.g., `func * <T:BinaryInteger>(_ lhs: Self, rhs: T) -> Self`)
/// which works with all `BinaryIntegers`, including `BigInt`s instead of
/// just `Int`s or a single integer type.
///

extension BInt : SignedInteger {
public static var isSigned: Bool { true }

public init(integerLiteral value: Int) {
self.init(value)
}

public init<T>(_ source: T) where T : BinaryInteger {
if let int = BInt(exactly: source) {
self = int
} else {
self.init(0)
}
}

public init<T>(clamping source: T) where T : BinaryInteger {
self.init(source)
}

public init<T>(truncatingIfNeeded source: T) where T : BinaryInteger {
self.init(source)
}

public init?<T>(exactly source: T) where T : BinaryInteger {
var isNegative = false
if source.signum() < 0 {
isNegative = true
}
let words = source.words
var bwords = Limbs()
for word in words {
if isNegative { bwords.append(UInt(~word)) }
else { bwords.append(UInt(word)) }
}
self.init(bwords, false)
if isNegative { self += 1; self.negate() }
}

public init?<T>(exactly source: T) where T : BinaryFloatingPoint {
guard source.isFinite else { return nil }
if source.rounded() != source { return nil }
if source.isZero { self.init(0); return }
self.init(source)
}

public init<T>(_ source: T) where T : BinaryFloatingPoint {
// FIXME: - Support other types of BinaryFloatingPoint
if let bint = BInt(Double(source)) {
self = bint
} else {
self.init(0)
}
}
}

extension BInt : Numeric {
public var magnitude: BInt { BInt(words) }
}

extension BInt : BinaryInteger {
public static func <<= <RHS:BinaryInteger>(lhs: inout BInt, rhs: RHS) {
lhs = lhs << Int(rhs)
}

public static func >>= <RHS:BinaryInteger>(lhs: inout BInt, rhs: RHS) {
lhs = lhs >> Int(rhs)
}
}

/// Add support for `StaticBigInt` - 24 Jun 2023 - MG
/// Currently disabled due to Swift Playground incompatiblity
/// Uncomment to enable `StaticBigInt` support (i.e., huge integer literals).
//@available(macOS 13.3, *)
//extension BInt : ExpressibleByIntegerLiteral {
// public init(integerLiteral value: StaticBigInt) {
// let isNegative = value.signum() < 0
// let bitWidth = value.bitWidth
// if bitWidth < Int.bitWidth {
// self.init(Int(bitPattern: value[0]))
// } else {
// precondition(value[0].bitWidth == 64, "Requires 64-bit Ints!")
// let noOfWords = (bitWidth / 64) + 1 // must be 64-bit system
// var words = Limbs()
// for index in 0..<noOfWords {
// // StaticBigInt words are 2's complement so negative
// // values needed to be inverted and have one added
// if isNegative { words.append(UInt64(~value[index])) }
// else { words.append(UInt64(value[index])) }
// }
// self.init(words, false)
// if isNegative { self += 1; self.negate() }
// }
// }
//}
Loading

0 comments on commit a70db7b

Please sign in to comment.