Skip to content

Instantly share code, notes, and snippets.

@katoy
Last active August 29, 2015 14:13
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 katoy/bf44febb4d35e1d158c9 to your computer and use it in GitHub Desktop.
Save katoy/bf44febb4d35e1d158c9 to your computer and use it in GitHub Desktop.
Swift での 分数 (Rational number)
#!/usr/bin/xcrun swift
// See https://gist.github.com/kristopherjohnson/eb7dc5b6af06eb42ef38
// http://codereview.stackexchange.com/questions/56872/fraction-rational-number-structure-with-custom-operators
import Cocoa
public struct Rational {
let numer: Int
let denom: Int
public init(_ numerator: Int = 0, _ denominator: Int = 1) {
assert(denominator != 0)
let g = Rational.gcd(abs(numerator), abs(denominator))
numer = abs(numerator / g)
denom = abs(denominator / g)
let sign = ((numerator >= 0 && denominator > 0) || (numerator <= 0 && denominator < 0)) ? 1: -1
numer *= sign
}
// 小数(循環小数)表記から Rational に変換する
public init(_ str: String) {
assert(countElements(str) > 0)
let strs = split(str, {$0 == "."})
if strs.count == 1 {
numer = abs(strs[0].toInt()!)
denom = 1
} else if str.rangeOfString("{") != nil {
let subs = countElements(strs) == 1 ? split(strs[0], {$0 == "{"}) : split(strs[1], {$0 == "{"})
let reps = countElements(subs) == 1 ? split(subs[0], {$0 == "}"}) : split(subs[1], {$0 == "}"})
let rep = countElements(reps) == 1 ? "\(reps[0])" : "\(reps[1])" // 巡回部分
let works = split(strs[0], {$0 == "-"})
let work = countElements(works) == 1 ? works[0] : works[1]
let str1 = countElements(subs) == 1 ? "\(work)" : "\(work)\(subs[0])" // 書数部分
let str0 = countElements(subs) == 1 ? "\(str1)\(rep)" : "\(str1)\(rep)" // 整数部分
let keta1 = Double(countElements(str1))
let keta2 = Double(countElements(reps[0]))
let denominator = abs(Int((pow(10.0, keta2) - 1.0) * pow(10.0, keta1 - 1)))
let numerator = abs("\(str0)".toInt()! - "\(str1)".toInt()!)
let g = Rational.gcd(numerator, denominator)
numer = numerator / g
denom = denominator / g
} else {
let str0 = "\(strs[0])\(strs[1])"
let numerator = abs(str0.toInt()!)
let keta = (str0.rangeOfString("-") != nil) ? Double(countElements(str0)) - 1 : Double(countElements(str0))
let denominator = abs(Int(pow(10.0, keta - 1)))
let g = Rational.gcd(numerator, denominator)
numer = numerator / g
denom = denominator / g
}
if str.rangeOfString("-") != nil {
numer *= -1
}
}
private static func gcd(a: Int, _ b: Int) -> Int {
return b == 0 ? a : gcd(b, a % b)
}
// 小数 (循環小数) 表記にする。 (循環部は {} で囲む)
func toPeriodString() -> String {
var results:[Int] = []
var mods: [Int] = []
var a = abs(numer)
let b = abs(denom)
var pos: Int?
while true {
let p = a / b
let q = a % b
if q == 0 {
results.append(p)
break
} else {
pos = find(mods, q)
if pos != nil {
results.append(p)
break
} else {
results.append(p)
mods.append(q)
a = q * 10
}
}
}
var str = ""
if pos != nil { // 循環小数
let res1 = (pos! > 0) ? Array(results[1...(pos!)]) : []
let res2 = Array(results[(pos! + 1)...(results.count - 1)])
let n0 = results[0] // 整数部分
let sub1 = (res1.count > 0) ? res1.reduce("") { "\($0)\($1)" } : ""
let sub2 = res2.reduce("") { "\($0)\($1)" }
str = "\(n0).\(sub1){\(sub2)}"
} else {
let n0 = results.removeAtIndex(0) // 整数部分
if results.count > 0 { // 小数
let sub = results.reduce("") { "\($0)\($1)" }
str = "\(n0).\(sub)"
} else { // 整数
str = "\(n0)"
}
}
if (numer < 0) {
str = "-" + str
}
return str
}
}
extension Rational: Printable {
public var description: String { return "\(numer)/\(denom)" }
}
func + (lhs: Rational, rhs: Rational) -> Rational {
return Rational(
lhs.numer * rhs.denom + rhs.numer * lhs.denom,
lhs.denom * rhs.denom
)
}
func + (lhs: Rational, rhs: Int) -> Rational {
return lhs + Rational(rhs)
}
func + (lhs: Int, rhs: Rational) -> Rational {
return Rational(lhs) + rhs
}
func - (lhs: Rational, rhs: Rational) -> Rational {
return Rational(
lhs.numer * rhs.denom - rhs.numer * lhs.denom,
lhs.denom * rhs.denom
)
}
func - (lhs: Rational, rhs: Int) -> Rational {
return lhs - Rational(rhs)
}
func - (lhs: Int, rhs: Rational) -> Rational {
return Rational(lhs) - rhs
}
func * (lhs: Rational, rhs: Rational) -> Rational {
return Rational(
lhs.numer * rhs.numer,
lhs.denom * rhs.denom
)
}
func * (lhs: Rational, rhs: Int) -> Rational {
return lhs * Rational(rhs)
}
func * (lhs: Int, rhs: Rational) -> Rational {
return Rational(lhs) * rhs
}
func / (lhs: Rational, rhs: Rational) -> Rational {
return Rational(
lhs.numer * rhs.denom,
lhs.denom * rhs.numer
)
}
func / (lhs: Rational, rhs: Int) -> Rational {
return lhs / Rational(rhs)
}
func / (lhs: Int, rhs: Rational) -> Rational {
return Rational(lhs) / rhs
}
func < (lhs: Rational, rhs: Rational) -> Bool {
return lhs.numer * rhs.denom < rhs.numer * lhs.denom
}
func += (inout lhs: Rational, rhs: Rational) {
lhs = lhs + rhs
}
func -= (inout lhs: Rational, rhs: Rational) {
lhs = lhs - rhs
}
func *= (inout lhs: Rational, rhs: Rational) {
lhs = lhs * rhs
}
func /= (inout lhs: Rational, rhs: Rational) {
lhs = lhs / rhs
}
postfix func ++ (inout val: Rational) -> Rational {
val += Rational(1)
return val
}
postfix func -- (inout val: Rational) -> Rational {
val -= Rational(1)
return val
}
extension Int {
var rational: Rational {
return Rational(self)
}
}
// Examples
//---------------------------
let initVal = Rational()
assert(initVal.description == "0/1")
let integerVal = Rational(3)
assert(integerVal.description == "3/1")
let negIntegerVal = Rational(-3)
assert(negIntegerVal.description == "-3/1")
let oneHalf = Rational(1, 2)
assert(oneHalf.description == "1/2")
let twoThirds = Rational(2, 3)
assert(twoThirds.description == "2/3")
let sum = oneHalf + twoThirds
assert(sum.description == "7/6")
let product = oneHalf * twoThirds
assert(product.description == "1/3")
let expr = oneHalf + oneHalf * twoThirds
assert(expr.description == "5/6")
let t3 = Rational(3)
assert(t3.description == "3/1")
let frac = Rational(66, 42)
assert(frac.description == "11/7")
let sumInt = oneHalf + 1
assert(sumInt.description == "3/2")
let neg = Rational(1, -2)
assert(neg.description == "-1/2")
var v1 = Rational(1, 2)
let v2 = Rational(1, 3)
let v2Neg = Rational(-1, 3)
v1 += v2
assert(v1.description == "5/6")
v1 += v2Neg
assert(v1.description == "1/2")
v1 -= v2
assert(v1.description == "1/6")
v1 -= v2Neg
assert(v1.description == "1/2")
v1 *= v2
assert(v1.description == "1/6")
v1 *= v2Neg
assert(v1.description == "-1/18")
v1 /= v2
assert(v1.description == "-1/6")
v1 /= v2Neg
assert(v1.description == "1/2")
v1++
assert(v1.description == "3/2")
v1--
assert(v1.description == "1/2")
//assert(3.rational.description == "3/1")
//assert((-3).rational.description == "-3/1")
assert(Rational(1, 11).toPeriodString() == "0.{09}")
assert(Rational(1, 10).toPeriodString() == "0.1")
assert(Rational(1, 9).toPeriodString() == "0.{1}")
assert(Rational(1, 8).toPeriodString() == "0.125")
assert(Rational(1, 7).toPeriodString() == "0.{142857}")
assert(Rational(1, 6).toPeriodString() == "0.1{6}")
assert(Rational(1, 5).toPeriodString() == "0.2")
assert(Rational(1, 4).toPeriodString() == "0.25")
assert(Rational(1, 3).toPeriodString() == "0.{3}")
assert(Rational(1, 2).toPeriodString() == "0.5")
assert(Rational(1, 1).toPeriodString() == "1")
assert(Rational(101, 2).toPeriodString() == "50.5")
assert(Rational(100, 2).toPeriodString() == "50")
assert(Rational(101, 3).toPeriodString() == "33.{6}")
assert(Rational(101, -3).toPeriodString() == "-33.{6}")
assert(Rational(0, -3).toPeriodString() == "0")
assert(Rational(355, 113).toPeriodString() == "3.{1415929203539823008849557522123893805309734513274336283185840707964601769911504424778761061946902654867256637168}")
assert(Rational("123").description == "123/1")
assert(Rational("-123").description == "-123/1")
assert(Rational("1.5").description == "3/2")
assert(Rational("-1.5").description == "-3/2")
assert(Rational("0.1{6}").description == "1/6")
assert(Rational("2.1{6}").description == "13/6")
assert(Rational("1.{6}").description == "5/3")
assert(Rational("0.01{6}").description == "1/60")
assert(Rational("-0.01{6}").description == "-1/60")
assert(Rational("0.{142857}").description == "1/7")
// assert(Rational("3.{1415929203539823008849557522123893805309734513274336283185840707964601769911504424778761061946902654867256637168}").description == "355/113")
// v1 = Rational("3.{1415929203539823008849557522123893805309734513274336283185840707964601769911504424778761061946902654867256637168}")
// println(v1.description)
// println(v1.toPeriodString())
let x = Rational(10)
let y = Rational(3)
println((x / y).description)
println(((x / y) * y).description)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment