Skip to content

Instantly share code, notes, and snippets.

@bppr
Created February 8, 2018 16:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bppr/9cd96215d04e2f9562ee58fd8e21184e to your computer and use it in GitHub Desktop.
Save bppr/9cd96215d04e2f9562ee58fd8e21184e to your computer and use it in GitHub Desktop.
Ratio.swift
// expressible as integer literal
// due to type inference from our variable declaration,
// 1/5 really evaluates to Ratio(1) / Ration(5),
// which returns Ratio(1, 5)
let r: Ratio = 1/5 // => 1/5
// simple arithmetic operations available
r + 1/5 // => 2/5
r / 5 // => 1/25
// displays whole numbers without a denominator
r * -5 // => -1
var r2: Ratio = 1/4 // => 1/4
// allows re-assignment arithmetic operators
r2 /= 4 // => 1/16
r2 += 1/16 // => 1/8
// can be equated to other rationals
r2 == 1/8 // => true
// can be compared to other rationals
r2 < 1/4 // => true
// can be converted to decimal types
// as with all fp operations, potential precision-loss
r2.asFloat // => 0.125
// normalizes negativity to numerator-side
let n: Ratio = 1 / -5 // => -1/5
fileprivate func gcd(_ a: Int, _ b: Int) -> Int {
return b == 0 ? a : gcd(b, a % b)
}
fileprivate func abs(_ n: Int) -> Int {
return n > 0 ? n : -n
}
struct Ratio {
let numerator: Int
let denominator: Int
init(_ numerator: Int, _ denominator: Int = 1) {
var (numerator, denominator) = (numerator, denominator)
let divisor = gcd(numerator, denominator)
numerator /= divisor
denominator /= divisor
if denominator < 0 {
(numerator, denominator) = (-numerator, -denominator)
}
self.numerator = numerator
self.denominator = denominator
}
var asDouble: Double { return Double(numerator) / Double(denominator) }
var asFloat: Float { return Float(numerator) / Float(denominator) }
var abs: Ratio { return self.numerator < 0 ? -self : self }
}
extension Ratio: CustomDebugStringConvertible {
var debugDescription: String {
let denom = denominator == 1 ? "" : "/\(String(describing: denominator))"
return "\(numerator)\(denom)"
}
}
extension Ratio: ExpressibleByIntegerLiteral {
init(integerLiteral value: IntegerLiteralType) {
self.init(value)
}
}
extension Ratio: Equatable {}
func ==(lhs: Ratio, rhs: Ratio) -> Bool {
return lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator
}
extension Ratio: Comparable {}
func <(lhs: Ratio, rhs: Ratio) -> Bool {
return (rhs.abs - lhs.abs).numerator > 0
}
func +(lhs: Ratio, rhs: Ratio) -> Ratio {
return Ratio(
lhs.numerator * rhs.denominator + rhs.numerator * lhs.denominator,
lhs.denominator * rhs.denominator
)
}
func +=(lhs: inout Ratio, rhs: Ratio) {
lhs = lhs + rhs
}
prefix func -(r: Ratio) -> Ratio {
return Ratio(-r.numerator, r.denominator)
}
func -(lhs: Ratio, rhs: Ratio) -> Ratio {
return lhs + (-rhs)
}
func -=(lhs: inout Ratio, rhs: Ratio) {
lhs = lhs - rhs
}
func *(lhs: Ratio, rhs: Ratio) -> Ratio {
return Ratio(lhs.numerator * rhs.numerator,lhs.denominator * rhs.denominator)
}
func *=(lhs: inout Ratio, rhs: Ratio) {
lhs = lhs * rhs
}
func /(lhs: Ratio, rhs: Ratio) -> Ratio {
return lhs * Ratio(rhs.denominator, rhs.numerator)
}
func /=(lhs: inout Ratio, rhs: Ratio) {
lhs = lhs / rhs
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment