197 lines
5.7 KiB
Swift
197 lines
5.7 KiB
Swift
enum Num {
|
|
case int(Int)
|
|
case big(BigInt)
|
|
case rat(BigInt, BigInt)
|
|
case real(Double)
|
|
}
|
|
|
|
func asNum(_ value: Value, _ who: String) throws -> Num {
|
|
switch value {
|
|
case .integer(let n): return .int(n)
|
|
case .bigint(let b): return .big(b)
|
|
case .rational(let n, let d): return .rat(n, d)
|
|
case .real(let d): return .real(d)
|
|
default: throw schemeError("\(who): expected number", [value])
|
|
}
|
|
}
|
|
|
|
func numToDouble(_ n: Num) -> Double {
|
|
switch n {
|
|
case .int(let i): return Double(i)
|
|
case .big(let b): return b.toDouble()
|
|
case .rat(let a, let b): return a.toDouble() / b.toDouble()
|
|
case .real(let d): return d
|
|
}
|
|
}
|
|
|
|
func numToValue(_ n: Num) -> Value {
|
|
switch n {
|
|
case .int(let i): return .integer(i)
|
|
case .big(let b): return normalizeBig(b)
|
|
case .rat(let a, let b): return makeRatio(a, b)
|
|
case .real(let d): return .real(d)
|
|
}
|
|
}
|
|
|
|
func asNumFromValue(_ value: Value) -> Num {
|
|
switch value {
|
|
case .integer(let n): return .int(n)
|
|
case .bigint(let b): return .big(b)
|
|
case .rational(let n, let d): return .rat(n, d)
|
|
case .real(let d): return .real(d)
|
|
default: return .int(0)
|
|
}
|
|
}
|
|
|
|
private func isReal(_ n: Num) -> Bool {
|
|
if case .real = n { return true }
|
|
return false
|
|
}
|
|
|
|
private func exactParts(_ n: Num) -> (BigInt, BigInt) {
|
|
switch n {
|
|
case .int(let i): return (BigInt(i), .one)
|
|
case .big(let b): return (b, .one)
|
|
case .rat(let a, let b): return (a, b)
|
|
case .real: return (.zero, .one)
|
|
}
|
|
}
|
|
|
|
private func reduceExact(_ numerator: BigInt, _ denominator: BigInt) -> Num {
|
|
var num = numerator
|
|
var den = denominator
|
|
if den.isNegative {
|
|
num = num.negated
|
|
den = den.negated
|
|
}
|
|
let divisor = BigInt.gcd(num, den)
|
|
if !divisor.isZero && !divisor.isOne {
|
|
num = num.quotient(divisor)
|
|
den = den.quotient(divisor)
|
|
}
|
|
if den.isOne {
|
|
return num.fitsInt ? .int(num.toInt()) : .big(num)
|
|
}
|
|
return .rat(num, den)
|
|
}
|
|
|
|
func numAdd(_ a: Num, _ b: Num) -> Num {
|
|
if case .int(let x) = a, case .int(let y) = b {
|
|
let (r, over) = x.addingReportingOverflow(y)
|
|
return over ? .big(BigInt(x) + BigInt(y)) : .int(r)
|
|
}
|
|
if isReal(a) || isReal(b) {
|
|
return .real(numToDouble(a) + numToDouble(b))
|
|
}
|
|
let (an, ad) = exactParts(a)
|
|
let (bn, bd) = exactParts(b)
|
|
return reduceExact(an * bd + bn * ad, ad * bd)
|
|
}
|
|
|
|
func numSub(_ a: Num, _ b: Num) -> Num {
|
|
if case .int(let x) = a, case .int(let y) = b {
|
|
let (r, over) = x.subtractingReportingOverflow(y)
|
|
return over ? .big(BigInt(x) - BigInt(y)) : .int(r)
|
|
}
|
|
if isReal(a) || isReal(b) {
|
|
return .real(numToDouble(a) - numToDouble(b))
|
|
}
|
|
let (an, ad) = exactParts(a)
|
|
let (bn, bd) = exactParts(b)
|
|
return reduceExact(an * bd - bn * ad, ad * bd)
|
|
}
|
|
|
|
func numMul(_ a: Num, _ b: Num) -> Num {
|
|
if case .int(let x) = a, case .int(let y) = b {
|
|
let (r, over) = x.multipliedReportingOverflow(by: y)
|
|
return over ? .big(BigInt(x) * BigInt(y)) : .int(r)
|
|
}
|
|
if isReal(a) || isReal(b) {
|
|
return .real(numToDouble(a) * numToDouble(b))
|
|
}
|
|
let (an, ad) = exactParts(a)
|
|
let (bn, bd) = exactParts(b)
|
|
return reduceExact(an * bn, ad * bd)
|
|
}
|
|
|
|
func numDiv(_ a: Num, _ b: Num) throws -> Num {
|
|
if isReal(a) || isReal(b) {
|
|
return .real(numToDouble(a) / numToDouble(b))
|
|
}
|
|
let (an, ad) = exactParts(a)
|
|
let (bn, bd) = exactParts(b)
|
|
if bn.isZero { throw schemeError("division by zero") }
|
|
return reduceExact(an * bd, ad * bn)
|
|
}
|
|
|
|
func numCompare(_ a: Num, _ b: Num) -> Int {
|
|
if case .int(let x) = a, case .int(let y) = b {
|
|
return x == y ? 0 : (x < y ? -1 : 1)
|
|
}
|
|
if isReal(a) || isReal(b) {
|
|
let x = numToDouble(a)
|
|
let y = numToDouble(b)
|
|
return x == y ? 0 : (x < y ? -1 : 1)
|
|
}
|
|
let (an, ad) = exactParts(a)
|
|
let (bn, bd) = exactParts(b)
|
|
let left = an * bd
|
|
let right = bn * ad
|
|
if left == right { return 0 }
|
|
return left < right ? -1 : 1
|
|
}
|
|
|
|
func asExactInteger(_ value: Value, _ who: String) throws -> BigInt {
|
|
switch value {
|
|
case .integer(let n): return BigInt(n)
|
|
case .bigint(let b): return b
|
|
case .real(let d) where d == d.rounded() && d.isFinite:
|
|
return exactIntegerFromDouble(d)
|
|
default:
|
|
throw schemeError("\(who): expected integer", [value])
|
|
}
|
|
}
|
|
|
|
func exactIntegerFromDouble(_ d: Double) -> BigInt {
|
|
if d == 0 { return .zero }
|
|
let negative = d < 0
|
|
var magnitude = abs(d)
|
|
var result = BigInt.zero
|
|
let chunk = 4294967296.0
|
|
var scale = BigInt.one
|
|
while magnitude >= 1 {
|
|
let low = magnitude.truncatingRemainder(dividingBy: chunk)
|
|
result = result + BigInt(Int(low)) * scale
|
|
magnitude = (magnitude - low) / chunk
|
|
scale = scale * BigInt(Int(chunk))
|
|
}
|
|
return negative ? result.negated : result
|
|
}
|
|
|
|
func exactFromDouble(_ d: Double) -> Value {
|
|
if d == d.rounded() && abs(d) < 9.007199254740992e15 {
|
|
return .integer(Int(d))
|
|
}
|
|
if d == d.rounded() {
|
|
return normalizeBig(exactIntegerFromDouble(d))
|
|
}
|
|
let bits = d.bitPattern
|
|
let sign = (bits >> 63) == 1
|
|
let exponent = Int((bits >> 52) & 0x7ff)
|
|
let fractionBits = bits & 0xf_ffff_ffff_ffff
|
|
var mantissa: BigInt
|
|
var twoExponent: Int
|
|
if exponent == 0 {
|
|
mantissa = BigInt(Int(fractionBits))
|
|
twoExponent = -1074
|
|
} else {
|
|
mantissa = BigInt(Int(fractionBits | 0x10_0000_0000_0000))
|
|
twoExponent = exponent - 1075
|
|
}
|
|
if sign { mantissa = mantissa.negated }
|
|
if twoExponent >= 0 {
|
|
return normalizeBig(mantissa * BigInt(2).power(twoExponent))
|
|
}
|
|
return makeRatio(mantissa, BigInt(2).power(-twoExponent))
|
|
}
|