Last active
August 29, 2015 14:13
-
-
Save katoy/bf44febb4d35e1d158c9 to your computer and use it in GitHub Desktop.
Swift での 分数 (Rational number)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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