diff --git a/Sources/SternBroctTreeSwift/Core/Fraction.swift b/Sources/SternBroctTreeSwift/Core/Fraction.swift index b8c8948..52680f9 100644 --- a/Sources/SternBroctTreeSwift/Core/Fraction.swift +++ b/Sources/SternBroctTreeSwift/Core/Fraction.swift @@ -64,3 +64,30 @@ public protocol Fraction : SBTreeNode, SignedNumeric, Comparable, Hashable { func simplified() -> Self } + +extension Fraction where Number : BinaryInteger { + + /// Returns a Boolean value whether this fraction is unit fraction or not. + public var isUnit: Bool { + return numerator == 1 + } + + /// Returns a Boolean value whther this fraction is proper fraction or not. + public var isProper: Bool { + return numerator < denominator + } + + /// Returns a Boolean value whether this fraction is improper or not. + public var isImproper: Bool { + return denominator > numerator + } + + /// Returns a mixed fraction from this value. + /// + /// - Returns: The result touple cantains two values intergetPart and fraction. + /// The intergetPart is equal to `intergerPartOfMixedFraction` and the fraction numerator is equal to `numeratorOfMixedFraction` property. + public func mixed() -> (integerPart: Number, fraction: Self) { + return (numerator / denominator, Self(numerator: numerator % denominator, denominator: denominator)) + } + +} diff --git a/Sources/SternBroctTreeSwift/Core/NSRational.swift b/Sources/SternBroctTreeSwift/Core/NSRational.swift index b03502c..f74da18 100644 --- a/Sources/SternBroctTreeSwift/Core/NSRational.swift +++ b/Sources/SternBroctTreeSwift/Core/NSRational.swift @@ -8,7 +8,7 @@ import Foundation /// A rational type for reference semantics. The type stores and uses value rational internally. -public final class NSRational : NSObject, NSSecureCoding, SignedRational { +public final class NSRational : NSObject, NSSecureCoding, SignedRational, SBTreeNode { public typealias Number = Int diff --git a/Sources/SternBroctTreeSwift/Core/Rational.swift b/Sources/SternBroctTreeSwift/Core/Rational.swift index 5be5066..ec8cb68 100644 --- a/Sources/SternBroctTreeSwift/Core/Rational.swift +++ b/Sources/SternBroctTreeSwift/Core/Rational.swift @@ -44,3 +44,5 @@ public struct Rational : MutableSignedRational { } } + +extension Rational : SBTreeNode { } diff --git a/Sources/SternBroctTreeSwift/Core/Rational16.swift b/Sources/SternBroctTreeSwift/Core/Rational16.swift index 0513226..1dfdd24 100644 --- a/Sources/SternBroctTreeSwift/Core/Rational16.swift +++ b/Sources/SternBroctTreeSwift/Core/Rational16.swift @@ -42,3 +42,4 @@ public struct Rational16 : MutableSignedRational { } +extension Rational16 : SBTreeNode { } diff --git a/Sources/SternBroctTreeSwift/Core/Rational32.swift b/Sources/SternBroctTreeSwift/Core/Rational32.swift index ea409ac..d79d024 100644 --- a/Sources/SternBroctTreeSwift/Core/Rational32.swift +++ b/Sources/SternBroctTreeSwift/Core/Rational32.swift @@ -42,3 +42,4 @@ public struct Rational32 : MutableSignedRational { } +extension Rational32 : SBTreeNode { } diff --git a/Sources/SternBroctTreeSwift/Core/Rational64.swift b/Sources/SternBroctTreeSwift/Core/Rational64.swift index fce4d92..0f0a0b2 100644 --- a/Sources/SternBroctTreeSwift/Core/Rational64.swift +++ b/Sources/SternBroctTreeSwift/Core/Rational64.swift @@ -42,3 +42,4 @@ public struct Rational64 : MutableSignedRational { } +extension Rational64 : SBTreeNode { } diff --git a/Sources/SternBroctTreeSwift/Core/Rational8.swift b/Sources/SternBroctTreeSwift/Core/Rational8.swift index c4b2621..c9bf8d0 100644 --- a/Sources/SternBroctTreeSwift/Core/Rational8.swift +++ b/Sources/SternBroctTreeSwift/Core/Rational8.swift @@ -42,3 +42,4 @@ public struct Rational8 : MutableSignedRational { } +extension Rational8 : SBTreeNode { } diff --git a/Sources/SternBroctTreeSwift/Core/SignedRational.swift b/Sources/SternBroctTreeSwift/Core/SignedRational.swift index 839b1c6..0208ae0 100644 --- a/Sources/SternBroctTreeSwift/Core/SignedRational.swift +++ b/Sources/SternBroctTreeSwift/Core/SignedRational.swift @@ -13,25 +13,6 @@ import Foundation /// like `addingReportingOverflow(:)` if you consider about overflow. public protocol SignedRational : Fraction, CustomFloatConvertible, CustomDoubleConvertible, CustomDecimalConvertible where Number : SignedInteger & FixedWidthInteger { - /// Returns a mediant from two fractions. - static func mediant(left: Self, right: Self) -> Self - - /// Retuns the result of mediant of the given left and right, along with a Boolean value indicating whether overflow occurred in the operation. - /// - /// - Parameters: - /// - left: The left value to mediant. - /// - right: The right value to mediant. - static func mediantReportingOverflow(left: Self, right: Self) -> (partialValue: Self, overflow: Bool) - - /// Returns a boolean value whether this and the other are adjacent. - /// - /// - Parameter other: The other concrete rational to determine adjacent. - /// - Returns: The two values are adjacent or not. - func isAdjacent(to other: Self) -> Bool - - /// Returns an array of R or L sequence which are backwardeded from this rational. - func backwardingMatrixSequence() -> [Matrix2x2] - /// Returns the sum of this value and the given value, along with a Boolean value indicating whether overflow occurred in the operation. /// /// - Parameter other: The value to add to this value. @@ -86,58 +67,6 @@ extension SignedRational { return commonFactor != 1 && commonFactor != -1 } - /// Returns a mediant from two fractions. - public static func mediant(left: Self, right: Self) -> Self { - let numerator = left.numerator + right.numerator - let denominator = left.denominator + right.denominator - return Self(numerator: numerator, denominator: denominator) - } - - /// Returns a mediant from two fractions. - public static func mediantReportingOverflow(left: Self, right: Self) -> (partialValue: Self, overflow: Bool) { - let (numeratorAddingResult, numeratorAddingOverflow) = left.numerator.addingReportingOverflow(right.numerator) - let (denominatorAddingResult, denominatorAddingOverflow) = left.denominator.addingReportingOverflow(right.denominator) - - if numeratorAddingOverflow || denominatorAddingOverflow { - return (Self(numerator: numeratorAddingResult, denominator: denominatorAddingResult), true) - } - - return (Self(numerator: numeratorAddingResult,denominator: denominatorAddingResult), false) - } - - public func backwardingMatrixSequence() -> [Matrix2x2] { - - // Start from R. - var mixPartSequence: [Number] = [] - var continueFraction = self - - while continueFraction.numerator > 1 || continueFraction.denominator > 1 { - let isEndIndeciesOfContinueFraction = continueFraction.numerator == 2 && continueFraction.denominator == 1 - if isEndIndeciesOfContinueFraction { - mixPartSequence.append(1) - break - } else { - mixPartSequence.append(continueFraction.mixedPart) - } - continueFraction = Self(numerator: continueFraction.denominator, denominator: continueFraction.mixedRemainder) - } - - let boxOfRLMatrixes: [[Matrix2x2]] = mixPartSequence.enumerated().compactMap({ index, value in - guard value > 0 else { - return nil - } - - let isEven = index % 2 == 0 - if isEven || index == 0 { - return Array(repeating: Matrix2x2.R, count: Int(value)) - } else { - return Array(repeating: Matrix2x2.L, count: Int(value)) - } - }) - - return boxOfRLMatrixes.flatMap({ $0 }) - } - public func comparedReportingOverflow(with other: Self) -> (partialValue: RationalComparisonResult, overflow: Bool) { let a = Number(numerator) let b = Number(denominator) @@ -170,60 +99,6 @@ extension SignedRational { } -/// Default implementation for SBTreeNode. -extension SignedRational { - - /// Returns a boolean value whether this and the other are adjacent. - /// - /// - Parameter other: The other concrete rational to determine adjacent. - /// - Returns: The two values are adjacent or not. - public func isAdjacent(to other: Self) -> Bool { - let (ad, adOverflow) = Number(numerator).multipliedReportingOverflow(by: Number(other.denominator)) - let (bc, bcOverflow) = Number(denominator).multipliedReportingOverflow(by: Number(other.numerator)) - - if !adOverflow && !bcOverflow { - return abs(ad - bc) == 1 - } else { - return false - } - } - - /// Returns simplicity of a rational. - /// - /// if **r=a/b** is in reduced form, **the simplicity of r** is defined to be **L(r)≡1/ab**. - public func simplicity() -> (partialValue: Self, overflow: Bool) { - let (ab, overflow) = Number(numerator).multipliedReportingOverflow(by: Number(denominator)) - return (Self("1/\(ab)"), overflow) - } - - /// Returns total of a rational. - /// - /// if **r=a/b** is in reduced form, define the total of r to be **t(r) ≡ a+b**. - public var total: Number { - return numerator + denominator - } - - /// Returns the interger value of mixed rational. - public var mixedPart: Number { - return numerator / denominator - } - - /// Returns the numerator of mixed rational. - public var mixedRemainder: Number { - return numerator % denominator - } - -} - -/// Default implementation for Equtable. -extension SignedRational { - - public static func == (lhs: Self, rhs: Self) -> Bool { - return lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator - } - -} - /// Default implementation for Comparable. extension SignedRational { @@ -251,10 +126,10 @@ extension SignedRational { public func hash(into hasher: inout Hasher) { - // In reduced form, SBTree node's fruction must be identified in the tree. - let x = simplified() - hasher.combine(x.numerator) - hasher.combine(x.denominator) + // Rational should be hashed in reduced form. + let reducedForm = simplified() + hasher.combine(reducedForm.numerator) + hasher.combine(reducedForm.denominator) } } @@ -330,6 +205,100 @@ extension SignedRational where Magnitude == Double, IntegerLiteralType == Number } +/// Default implementation for SBTreeNode. +extension SignedRational where Self : SBTreeNode { + + /// Returns a mediant from two fractions. + public static func mediant(left: Self, right: Self) -> Self { + let numerator = left.numerator + right.numerator + let denominator = left.denominator + right.denominator + return Self(numerator: numerator, denominator: denominator) + } + + /// Retuns the result of mediant of the given left and right, along with a Boolean value indicating whether overflow occurred in the operation. + /// + /// - Parameters: + /// - left: The left value to mediant. + /// - right: The right value to mediant. + public static func mediantReportingOverflow(left: Self, right: Self) -> (partialValue: Self, overflow: Bool) { + let (numeratorAddingResult, numeratorAddingOverflow) = left.numerator.addingReportingOverflow(right.numerator) + let (denominatorAddingResult, denominatorAddingOverflow) = left.denominator.addingReportingOverflow(right.denominator) + + if numeratorAddingOverflow || denominatorAddingOverflow { + return (Self(numerator: numeratorAddingResult, denominator: denominatorAddingResult), true) + } + + return (Self(numerator: numeratorAddingResult,denominator: denominatorAddingResult), false) + } + + /// Returns an array of R or L sequence which are backwardeded from this rational. + public func backwardingMatrixSequence() -> [Matrix2x2] { + + // Start from R. + var mixPartSequence: [Number] = [] + var continueFraction = self + + while continueFraction.numerator > 1 || continueFraction.denominator > 1 { + let mixed = continueFraction.mixed() + let isEndIndeciesOfContinueFraction = continueFraction.numerator == 2 && continueFraction.denominator == 1 + if isEndIndeciesOfContinueFraction { + mixPartSequence.append(1) + break + } else { + mixPartSequence.append(mixed.integerPart) + } + continueFraction = Self(numerator: mixed.fraction.denominator, + denominator: mixed.fraction.numerator) + } + + let boxOfRLMatrixes: [[Matrix2x2]] = mixPartSequence.enumerated().compactMap({ index, value in + guard value > 0 else { + return nil + } + + let isEven = index % 2 == 0 + if isEven || index == 0 { + return Array(repeating: Matrix2x2.R, count: Int(value)) + } else { + return Array(repeating: Matrix2x2.L, count: Int(value)) + } + }) + + return boxOfRLMatrixes.flatMap({ $0 }) + } + + /// Returns a boolean value whether this and the other are adjacent. + /// + /// - Parameter other: The other concrete rational to determine adjacent. + /// - Returns: The two values are adjacent or not. + public func isAdjacent(to other: Self) -> Bool { + let (ad, adOverflow) = Number(numerator).multipliedReportingOverflow(by: Number(other.denominator)) + let (bc, bcOverflow) = Number(denominator).multipliedReportingOverflow(by: Number(other.numerator)) + + if !adOverflow && !bcOverflow { + return abs(ad - bc) == 1 + } else { + return false + } + } + + /// Returns simplicity of a rational. + /// + /// if **r=a/b** is in reduced form, **the simplicity of r** is defined to be **L(r)≡1/ab**. + public func simplicity() -> (partialValue: Self, overflow: Bool) { + let (ab, overflow) = Number(numerator).multipliedReportingOverflow(by: Number(denominator)) + return (Self("1/\(ab)"), overflow) + } + + /// Returns total of a rational. + /// + /// if **r=a/b** is in reduced form, define the total of r to be **t(r) ≡ a+b**. + public var total: Number { + return numerator + denominator + } + +} + extension SignedRational { @@ -347,3 +316,4 @@ extension SignedRational { } + diff --git a/Tests/SternBroctTreeSwiftTests/Fraction/FractionTests.swift b/Tests/SternBroctTreeSwiftTests/Fraction/FractionTests.swift index a75c94b..4acdb4b 100644 --- a/Tests/SternBroctTreeSwiftTests/Fraction/FractionTests.swift +++ b/Tests/SternBroctTreeSwiftTests/Fraction/FractionTests.swift @@ -165,26 +165,6 @@ class FractionTests: XCTestCase { XCTAssertEqual(f.denominator, 3) } - func testMixedPart() { - let f = Rational("11/3") - XCTAssertEqual(f.mixedPart, 3) - } - - func testMixedPartZero() { - let f = Rational("3/11") - XCTAssertEqual(f.mixedPart, 0) - } - - func testMixedRemainder() { - let f = Rational("11/3") - XCTAssertEqual(f.mixedRemainder, 2) - } - - func testMixedRemainderZer0() { - let f = Rational("3/3") - XCTAssertEqual(f.mixedRemainder, 0) - } - func testBackwardsMatrixSequnce1() { let rational = Rational("3/11") let matrixSequence = rational.backwardingMatrixSequence() @@ -199,4 +179,63 @@ class FractionTests: XCTestCase { XCTAssertEqual(matrixSequence, [.R, .R, .L, .L, .R, .L]) } + func testUnitFraction() { + let rational = Rational("1/9") + XCTAssertTrue(rational.isUnit) + } + + func testNotUnitFraction() { + let rational = Rational("2/9") + XCTAssertFalse(rational.isUnit) + } + + func testNotUnitFractionWhenZero() { + let rational = Rational("0/9") + XCTAssertFalse(rational.isUnit) + } + + func testProperFraction() { + let rational = Rational("1/3") + XCTAssertTrue(rational.isProper) + } + + func testNotProperFraction() { + let rational = Rational("4/3") + XCTAssertFalse(rational.isProper) + } + + func testNotProperFractionWhenEqual() { + let rational = Rational("3/3") + XCTAssertFalse(rational.isProper) + } + + func testImproperFraction() { + let rational = Rational("3/4") + XCTAssertTrue(rational.isImproper) + } + + func testNotImproperFraction() { + let rational = Rational("3/2") + XCTAssertFalse(rational.isImproper) + } + + func testNotImproperFractionWhenEqual() { + let rational = Rational("3/3") + XCTAssertFalse(rational.isImproper) + } + + func testMixedForm() { + let rational = Rational("7/3") + let result = rational.mixed() + XCTAssertEqual(result.integerPart, 2) + XCTAssertEqual(result.fraction.description, "1/3") + } + + func testMixedFormIntegerZero() { + let rational = Rational("2/3") + let result = rational.mixed() + XCTAssertEqual(result.integerPart, 0) + XCTAssertEqual(result.fraction.description, "2/3") + } + } diff --git a/Tests/SternBroctTreeSwiftTests/Rational/RationalArithmeticTests.swift b/Tests/SternBroctTreeSwiftTests/Rational/RationalArithmeticTests.swift index 20e890f..ce10ead 100644 --- a/Tests/SternBroctTreeSwiftTests/Rational/RationalArithmeticTests.swift +++ b/Tests/SternBroctTreeSwiftTests/Rational/RationalArithmeticTests.swift @@ -130,4 +130,30 @@ class RationalArithmeticTests: XCTestCase { XCTAssertTrue(result.overflow) } + // This is benefit of adopting SignedNumeric. The protocol use `public init(integerLiteral value: IntegerLiteralType)` to adding integer. + func testAddingToIntegerNumber() { + let r = Rational8("2/3") + let result = r + 1 + XCTAssertEqual(result.description, "5/3") + } + + func testIntegerNumberAddingToRational() { + let r = Rational8("2/3") + let result = 1 + r + XCTAssertEqual(result.description, "5/3") + } + + func testMultiplyByIntegerNumber() { + let r = Rational8("2/3") + let result = r * 3 + XCTAssertEqual(result.description, "6/3") + } + + func testIntegerNumberMultiplyByRational() { + let r = Rational8("2/3") + let result = 3 * r + XCTAssertEqual(result.description, "6/3") + } + + }