|
typealias Q = RationalNumber |
|
|
|
struct RationalNumber { |
|
private var numerator : Int |
|
private var denominator : Int |
|
|
|
init(numerator: Int, denominator: Int) { |
|
self.numerator = numerator |
|
self.denominator = denominator |
|
} |
|
|
|
init(_ value: Int) { |
|
self.numerator = value |
|
self.denominator = 1 |
|
} |
|
|
|
mutating func reduce() { |
|
let gcd = numerator.greatestCommonDivisor(with: denominator) |
|
self.numerator /= gcd |
|
self.denominator /= gcd |
|
if self.denominator.sign { |
|
numerator = -numerator |
|
denominator = -denominator |
|
} |
|
} |
|
|
|
var reduced : RationalNumber { |
|
var this = self |
|
this.reduce() |
|
return this |
|
} |
|
|
|
var max : RationalNumber { |
|
return RationalNumber(Int.max) |
|
} |
|
|
|
var min : RationalNumber { |
|
return RationalNumber(Int.min) |
|
} |
|
|
|
var sign : Bool { |
|
if numerator < 0 { |
|
return !(denominator < 0) |
|
} |
|
return denominator < 0 |
|
} |
|
|
|
var abs : RationalNumber { |
|
return RationalNumber( |
|
numerator: self.numerator.abs, |
|
denominator: self.denominator.abs) |
|
} |
|
} |
|
|
|
extension RationalNumber : IntegerLiteralConvertible { |
|
init(integerLiteral value: Int) { |
|
self.numerator = value |
|
self.denominator = 1 |
|
} |
|
} |
|
|
|
extension RationalNumber : Equatable {} |
|
|
|
extension RationalNumber : CustomStringConvertible { |
|
var description: String { |
|
if denominator == 1 { return "\(numerator)" } |
|
if denominator == 0 { return "nan" } |
|
return "\(numerator)/\(denominator)" |
|
} |
|
} |
|
|
|
|
|
|
|
extension RationalNumber { |
|
|
|
// source: http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions |
|
init(_ value: Double) { |
|
assert(value.isNormal) |
|
|
|
let sign = value < 0 |
|
var value = value.abs |
|
var numerator = 0 |
|
var denominator = 1 |
|
|
|
while value > 0 { |
|
let intValue = Int(value) |
|
value -= Double(intValue) |
|
numerator += intValue |
|
value *= 2 |
|
numerator *= 2 |
|
denominator *= 2 |
|
} |
|
if sign { numerator = -numerator } |
|
self = RationalNumber(numerator: numerator, denominator: denominator) |
|
} |
|
|
|
// source: http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions |
|
init(readable value: Double, accuracy: Double = 0.00000001) { |
|
let sign = value < 0 |
|
let val : Double |
|
if sign { |
|
val = -value |
|
} else { |
|
val = value |
|
} |
|
var n = 1 |
|
var d = 1 |
|
var frac = Double(n) / Double(d) |
|
|
|
while (frac - val).abs > accuracy { |
|
if frac < value { |
|
n += 1 |
|
} else { |
|
d += 1 |
|
n = Int(val * Double(d)) |
|
} |
|
frac = Double(n) / Double(d) |
|
} |
|
|
|
self = RationalNumber(numerator: sign ? -n : n, denominator: d) |
|
|
|
} |
|
} |
|
|
|
extension Int { |
|
var abs : Int { |
|
return self < 0 ? -self : self |
|
} |
|
|
|
var sign : Bool { |
|
return self < 0 |
|
} |
|
} |
|
|
|
extension Double { |
|
var abs : Double { |
|
return self < 0 ? -self : self |
|
} |
|
|
|
var sign : Bool { |
|
return self < 0 |
|
} |
|
} |
|
|
|
prefix func - (rhs: inout RationalNumber) { |
|
if rhs.denominator < 0 { |
|
rhs.denominator = -rhs.denominator |
|
} else { |
|
rhs.numerator = -rhs.numerator |
|
} |
|
} |
|
|
|
func *= (lhs: inout RationalNumber, rhs: RationalNumber) { |
|
let rhs = rhs.reduced |
|
lhs.reduce() |
|
lhs.denominator = rhs.denominator * lhs.denominator |
|
lhs.numerator = lhs.numerator * rhs.numerator |
|
lhs.reduce() |
|
} |
|
|
|
func * (lhs: RationalNumber, rhs: RationalNumber) -> RationalNumber { |
|
var lhs = lhs |
|
lhs *= rhs |
|
return lhs |
|
} |
|
|
|
func /= (lhs: inout RationalNumber, rhs: RationalNumber) { |
|
let rhs = rhs.reduced |
|
lhs.reduce() |
|
lhs.denominator = rhs.denominator * lhs.numerator |
|
lhs.numerator = lhs.numerator * rhs.denominator |
|
lhs.reduce() |
|
} |
|
|
|
func / (lhs: RationalNumber, rhs: RationalNumber) -> RationalNumber { |
|
var lhs = lhs |
|
lhs /= rhs |
|
return lhs |
|
} |
|
|
|
func += (lhs: inout RationalNumber, rhs: RationalNumber) { |
|
let rhs = rhs.reduced |
|
lhs.reduce() |
|
lhs.numerator = lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator |
|
lhs.denominator = rhs.denominator * lhs.denominator |
|
lhs.reduce() |
|
} |
|
|
|
func + (lhs: RationalNumber, rhs: RationalNumber) -> RationalNumber { |
|
var lhs = lhs |
|
lhs += rhs |
|
return lhs |
|
} |
|
|
|
func -= (lhs: inout RationalNumber, rhs: RationalNumber) { |
|
let rhs = rhs.reduced |
|
lhs.reduce() |
|
lhs.numerator = lhs.numerator * rhs.denominator - rhs.numerator * lhs.denominator |
|
lhs.denominator = rhs.denominator * lhs.denominator |
|
lhs.reduce() |
|
} |
|
|
|
func - (lhs: RationalNumber, rhs: RationalNumber) -> RationalNumber { |
|
var lhs = lhs |
|
lhs -= rhs |
|
return lhs |
|
} |
|
|
|
func == (lhs: RationalNumber, rhs: RationalNumber) -> Bool { |
|
if lhs.sign != rhs.sign { return false } |
|
if lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator { |
|
return true |
|
} |
|
return lhs.numerator == -rhs.numerator && lhs.denominator == -rhs.denominator |
|
} |
|
|
|
extension IntegerArithmetic where Self : IntegerLiteralConvertible { |
|
func greatestCommonDivisor(with other: Self) -> Self { |
|
var a = self > other ? self : other |
|
var b = self < other ? self : other |
|
while b != 0 { |
|
let t = b |
|
b = a % b |
|
a = t |
|
} |
|
return a |
|
} |
|
} |
|
|
|
extension Double { |
|
init(_ rat: RationalNumber) { |
|
self = Double(rat.numerator) / Double(rat.denominator) |
|
} |
|
} |
Possibly enhancing to "real" Numeric-protocol in the future
see: https://gist.github.com/pauljohanneskraft/56f3757bf4026a1330e601e3af167e24#file-numeric-swift