From 485927e481db831b4d2d1e89dbbdf9262eccaeb5 Mon Sep 17 00:00:00 2001 From: Joe Newton <> Date: Thu, 20 Feb 2020 10:41:30 -0500 Subject: [PATCH 1/2] Added arithmetic operations that ignore overflow for Complex where the scalar type conforms to FixedWidthInteger --- Sources/Complex/ComplexArithmetic.swift | 126 ++++++++++++++ Sources/Complex/Operators.swift | 12 ++ .../ComplexTests/ComplexArithmeticTests.swift | 163 ++++++++++++++++++ 3 files changed, 301 insertions(+) diff --git a/Sources/Complex/ComplexArithmetic.swift b/Sources/Complex/ComplexArithmetic.swift index 86de047..9bf6c79 100644 --- a/Sources/Complex/ComplexArithmetic.swift +++ b/Sources/Complex/ComplexArithmetic.swift @@ -103,6 +103,87 @@ extension Complex where Scalar: SignedNumeric { } } +extension Complex where Scalar: FixedWidthInteger { + + @_transparent + public static func &+ (lhs: Complex, rhs: Complex) -> Complex { + return Complex(real: lhs.real &+ rhs.real, imaginary: lhs.imaginary &+ rhs.imaginary) + } + + @_transparent + public static func &+= (lhs: inout Complex, rhs: Complex) { + lhs = lhs &+ rhs + } + + @_transparent + public static func &- (lhs: Complex, rhs: Complex) -> Complex { + return Complex(real: lhs.real &- rhs.real, imaginary: lhs.imaginary &- rhs.imaginary) + } + + @_transparent + public static func &-= (lhs: inout Complex, rhs: Complex) { + lhs = lhs &- rhs + } + + // + + @_transparent + public static func &+ (lhs: Complex, rhs: Scalar) -> Complex { + return Complex(real: lhs.real &+ rhs, imaginary: lhs.imaginary) + } + + @_transparent + public static func &+ (lhs: Scalar, rhs: Complex) -> Complex { + return Complex(real: lhs &+ rhs.real, imaginary: rhs.imaginary) + } + + @_transparent + public static func &+= (lhs: inout Complex, rhs: Scalar) { + lhs = lhs &+ rhs + } + + // + + @inlinable @inline(__always) + public static func .&+ (lhs: Complex, rhs: Complex) -> Complex { + return lhs &+ rhs + } + + @inlinable @inline(__always) + public static func .&+= (lhs: inout Complex, rhs: Complex) { + lhs = lhs .&+ rhs + } + + // + + @_transparent + public static func &- (lhs: Complex, rhs: Scalar) -> Complex { + return Complex(real: lhs.real &- rhs, imaginary: lhs.imaginary) + } + + @_transparent + public static func &- (lhs: Scalar, rhs: Complex) -> Complex { + return Complex(real: lhs &- rhs.real, imaginary: .zero &- rhs.imaginary) + } + + @_transparent + public static func &-= (lhs: inout Complex, rhs: Scalar) { + lhs = lhs &- rhs + } + + // + + @inlinable @inline(__always) + public static func .&- (lhs: Complex, rhs: Complex) -> Complex { + return lhs &- rhs + } + + @inlinable @inline(__always) + public static func .&-= (lhs: inout Complex, rhs: Complex) { + lhs = lhs .&- rhs + } +} + // MARK: - Multiplication extension Complex { @@ -150,6 +231,51 @@ extension Complex { } } +extension Complex where Scalar: FixedWidthInteger { + + @_transparent + public static func &* (lhs: Complex, rhs: Complex) -> Complex { + let real = (lhs.real &* rhs.real) &- (lhs.imaginary &* rhs.imaginary) + let imaginary = (lhs.real &* rhs.imaginary) &+ (lhs.imaginary &* rhs.real) + + return Complex(real: real, imaginary: imaginary) + } + + @_transparent + public static func &*= (lhs: inout Complex, rhs: Complex) { + lhs = lhs &* rhs + } + + // + + @_transparent + public static func &* (lhs: Complex, rhs: Scalar) -> Complex { + return Complex(real: lhs.real &* rhs, imaginary: lhs.imaginary &* rhs) + } + + @_transparent + public static func &* (lhs: Scalar, rhs: Complex) -> Complex { + return Complex(real: lhs &* rhs.real, imaginary: lhs &* rhs.imaginary) + } + + @_transparent + public static func &*= (lhs: inout Complex, rhs: Scalar) { + lhs = lhs &* rhs + } + + // + + @_transparent + public static func .&* (lhs: Complex, rhs: Complex) -> Complex { + return Complex(real: lhs.real &* rhs.real, imaginary: lhs.imaginary &* rhs.imaginary) + } + + @_transparent + public static func .&*= (lhs: inout Complex, rhs: Complex) { + lhs = lhs .&* rhs + } +} + // MARK: - Division (BinaryInteger) extension Complex where Scalar: BinaryInteger { diff --git a/Sources/Complex/Operators.swift b/Sources/Complex/Operators.swift index c5c567e..8cb684f 100644 --- a/Sources/Complex/Operators.swift +++ b/Sources/Complex/Operators.swift @@ -11,14 +11,26 @@ import Foundation infix operator .+: AdditionPrecedence infix operator .+=: AssignmentPrecedence +/// Component-wise addition, ignoring overflow +infix operator .&+: AdditionPrecedence +infix operator .&+=: AssignmentPrecedence + /// Component-wise subtraction infix operator .-: AdditionPrecedence infix operator .-=: AssignmentPrecedence +/// Component-wise subtraction, ignoring overflow +infix operator .&-: AdditionPrecedence +infix operator .&-=: AssignmentPrecedence + /// Component-wise multiplication infix operator .*: MultiplicationPrecedence infix operator .*=: AssignmentPrecedence +/// Component-wise multiplication, ignoring overflow +infix operator .&*: MultiplicationPrecedence +infix operator .&*=: AssignmentPrecedence + /// Component-wise division infix operator ./: MultiplicationPrecedence infix operator ./=: AssignmentPrecedence diff --git a/Tests/ComplexTests/ComplexArithmeticTests.swift b/Tests/ComplexTests/ComplexArithmeticTests.swift index 087ce2a..f8d2253 100644 --- a/Tests/ComplexTests/ComplexArithmeticTests.swift +++ b/Tests/ComplexTests/ComplexArithmeticTests.swift @@ -96,6 +96,19 @@ class ComplexArithmeticTests: XCTestCase { testAddition(Complex(real: 1.0, imaginary: 2.0), 3.0, Complex(real: 4.0, imaginary: 2.0)) } + func testAdditionIgnoringOverflow() { + testAdditionIgnoringOverflow(forType: Int8.self) + testAdditionIgnoringOverflow(forType: Int16.self) + testAdditionIgnoringOverflow(forType: Int32.self) + testAdditionIgnoringOverflow(forType: Int64.self) + testAdditionIgnoringOverflow(forType: Int.self) + testAdditionIgnoringOverflow(forType: UInt8.self) + testAdditionIgnoringOverflow(forType: UInt16.self) + testAdditionIgnoringOverflow(forType: UInt32.self) + testAdditionIgnoringOverflow(forType: UInt64.self) + testAdditionIgnoringOverflow(forType: UInt.self) + } + func testSubtraction() { testSubtraction(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: 2, imaginary: 2)) testSubtraction(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: 2, imaginary: 2)) @@ -128,6 +141,19 @@ class ComplexArithmeticTests: XCTestCase { testSubtraction(Complex(real: 3.0, imaginary: 4.0), 1.0, Complex(real: 2.0, imaginary: 4.0)) } + func testSubtractionIgnoringOverflow() { + testSubtractionIgnoringOverflow(forType: Int8.self) + testSubtractionIgnoringOverflow(forType: Int16.self) + testSubtractionIgnoringOverflow(forType: Int32.self) + testSubtractionIgnoringOverflow(forType: Int64.self) + testSubtractionIgnoringOverflow(forType: Int.self) + testSubtractionIgnoringOverflow(forType: UInt8.self) + testSubtractionIgnoringOverflow(forType: UInt16.self) + testSubtractionIgnoringOverflow(forType: UInt32.self) + testSubtractionIgnoringOverflow(forType: UInt64.self) + testSubtractionIgnoringOverflow(forType: UInt.self) + } + func testMultiplication() { testMultiplication(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: -5, imaginary: 10)) testMultiplication(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: -5, imaginary: 10)) @@ -160,6 +186,19 @@ class ComplexArithmeticTests: XCTestCase { testMultiplication(Complex(real: 3.0, imaginary: 4.0), 2.0) } + func testMultiplicationIgnoringOverflow() { + testMultiplicationIgnoringOverflow(forType: Int8.self) + testMultiplicationIgnoringOverflow(forType: Int16.self) + testMultiplicationIgnoringOverflow(forType: Int32.self) + testMultiplicationIgnoringOverflow(forType: Int64.self) + testMultiplicationIgnoringOverflow(forType: Int.self) + testMultiplicationIgnoringOverflow(forType: UInt8.self) + testMultiplicationIgnoringOverflow(forType: UInt16.self) + testMultiplicationIgnoringOverflow(forType: UInt32.self) + testMultiplicationIgnoringOverflow(forType: UInt64.self) + testMultiplicationIgnoringOverflow(forType: UInt.self) + } + func testComponentwiseMultiplication() { testComponentwiseMultiplication(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2)) testComponentwiseMultiplication(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2)) @@ -316,8 +355,62 @@ class ComplexArithmeticTests: XCTestCase { XCTAssertEqual(complex, result, file: file, line: line) } + private func testAdditionIgnoringOverflow(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger { + // The test is defined to "succeed" if it doesn't crash + let lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + let rhs = Complex(real: Scalar.max, imaginary: Scalar.max) + let rhs2 = Scalar.max + + _ = lhs &+ rhs + _ = rhs &+ lhs + _ = lhs .&+ rhs + _ = rhs .&+ lhs + _ = lhs &+ rhs2 + _ = rhs2 &+ lhs + + var complex = lhs + complex &+= rhs + + complex = rhs + complex &+= lhs + + complex = lhs + complex .&+= rhs + + complex = rhs + complex .&+= lhs + + if Scalar.isSigned { + // If the scalar is signed each min is the smallest negative number. Adding these + // two together would overflow. + let lhs = Complex(real: Scalar.min, imaginary: Scalar.min) + let rhs = Complex(real: Scalar.min, imaginary: Scalar.min) + + _ = lhs &+ rhs + _ = rhs &+ lhs + _ = lhs .&+ rhs + _ = rhs .&+ lhs + + var complex = lhs + complex &+= rhs + + complex = lhs + complex &+= rhs2 + + complex = rhs + complex &+= lhs + + complex = lhs + complex .&+= rhs + + complex = rhs + complex .&+= lhs + } + } + private func testSubtraction(_ lhs: Complex, _ rhs: Complex, _ result: Complex, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(lhs - rhs, result, file: file, line: line) + XCTAssertEqual(lhs .- rhs, result, file: file, line: line) var complex = lhs complex -= rhs @@ -368,6 +461,47 @@ class ComplexArithmeticTests: XCTestCase { XCTAssertEqual(complex, result, file: file, line: line) } + private func testSubtractionIgnoringOverflow(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger { + // The test is defined to "succeed" if it doesn't crash + let lhs = Complex(real: Scalar.min, imaginary: Scalar.min) + let rhs = Complex(real: Scalar.max, imaginary: Scalar.max) + let rhs2 = Scalar.max + let rhs3 = Scalar.min + + _ = lhs &- rhs + _ = lhs .&- rhs + _ = lhs &- rhs2 + _ = rhs3 &- rhs + + var complex = lhs + complex &-= rhs + + complex = lhs + complex &-= rhs2 + + complex = lhs + complex .&-= rhs + + if Scalar.isSigned { + // If the scalar is signed each min is the smallest negative number. Subtracting + // these from the max would overflow. + let lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + let rhs = Complex(real: Scalar.min, imaginary: Scalar.min) + + _ = lhs &- rhs + _ = lhs .&- rhs + + var complex = lhs + complex &-= rhs + + complex = lhs + complex &-= rhs3 + + complex = lhs + complex .&-= rhs + } + } + private func testMultiplication(_ lhs: Complex, _ rhs: Complex, _ result: Complex, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(lhs * rhs, result, file: file, line: line) XCTAssertEqual(rhs * lhs, result, file: file, line: line) @@ -381,6 +515,35 @@ class ComplexArithmeticTests: XCTestCase { XCTAssertEqual(complex, result, file: file, line: line) } + private func testMultiplicationIgnoringOverflow(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger { + // The test is defined to "succeed" if it doesn't crash + let lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + let rhs = Complex(real: Scalar.max, imaginary: Scalar.max) + let rhs2 = Scalar.max + + _ = lhs &* rhs + _ = rhs &* lhs + _ = lhs .&* rhs + _ = rhs .&* lhs + _ = lhs &* rhs2 + _ = rhs2 &* lhs + + var complex = lhs + complex &*= rhs + + complex = rhs + complex &*= lhs + + complex = lhs + complex &*= rhs2 + + complex = lhs + complex .&*= rhs + + complex = rhs + complex .&*= lhs + } + private func testMultiplication(_ lhs: Complex, _ rhs: Scalar, file: StaticString = #file, line: UInt = #line) { let result = Complex(real: lhs.real * rhs, imaginary: lhs.imaginary * rhs) XCTAssertEqual(lhs * rhs, result, file: file, line: line) From d89e95441ec118c06dca32c94e8e47432d2b4ae6 Mon Sep 17 00:00:00 2001 From: Joe Newton <> Date: Thu, 20 Feb 2020 11:16:53 -0500 Subject: [PATCH 2/2] Added trivial overflowing arithmetic functions for Complex where the scalar type conforms to FixedWidthInteger --- Sources/Complex/ComplexArithmetic.swift | 59 +++++ .../ComplexTests/ComplexArithmeticTests.swift | 221 ++++++++++++++++++ 2 files changed, 280 insertions(+) diff --git a/Sources/Complex/ComplexArithmetic.swift b/Sources/Complex/ComplexArithmetic.swift index 9bf6c79..fe4766f 100644 --- a/Sources/Complex/ComplexArithmetic.swift +++ b/Sources/Complex/ComplexArithmetic.swift @@ -184,6 +184,27 @@ extension Complex where Scalar: FixedWidthInteger { } } +extension Complex where Scalar: FixedWidthInteger { + + @_transparent + public func addingReportingOverflow(_ rhs: Complex) -> (partialValue: Complex, overflow: Bool) { + let real = self.real.addingReportingOverflow(rhs.real) + let imaginary = self.imaginary.addingReportingOverflow(rhs.imaginary) + let overflow = real.overflow || imaginary.overflow + + return (partialValue: Complex(real: real.partialValue, imaginary: imaginary.partialValue), overflow: overflow) + } + + @_transparent + public func subtractingReportingOverflow(_ rhs: Complex) -> (partialValue: Complex, overflow: Bool) { + let real = self.real.subtractingReportingOverflow(rhs.real) + let imaginary = self.imaginary.subtractingReportingOverflow(rhs.imaginary) + let overflow = real.overflow || imaginary.overflow + + return (partialValue: Complex(real: real.partialValue, imaginary: imaginary.partialValue), overflow: overflow) + } +} + // MARK: - Multiplication extension Complex { @@ -276,6 +297,25 @@ extension Complex where Scalar: FixedWidthInteger { } } +extension Complex where Scalar: FixedWidthInteger { + + @_transparent + public func componentwiseMultipliedFullWidth(by rhs: Complex) -> (high: Complex, low: Complex) { + let real = self.real.multipliedFullWidth(by: rhs.real) + let imaginary = self.imaginary.multipliedFullWidth(by: rhs.imaginary) + + return (high: Complex(real: real.high, imaginary: imaginary.high), low: Complex(real: real.low, imaginary: imaginary.low)) + } + + @_transparent + public func componentwiseMultipliedReportingOverflow(by rhs: Complex) -> (partialValue: Complex, overflow: Bool) { + let real = self.real.multipliedReportingOverflow(by: rhs.real) + let imaginary = self.imaginary.multipliedReportingOverflow(by: rhs.imaginary) + + return (partialValue: Complex(real: real.partialValue, imaginary: imaginary.partialValue), overflow: real.overflow || imaginary.overflow) + } +} + // MARK: - Division (BinaryInteger) extension Complex where Scalar: BinaryInteger { @@ -375,3 +415,22 @@ extension Complex where Scalar: FloatingPoint { lhs = lhs ./ rhs } } + +extension Complex where Scalar: FixedWidthInteger { + + @_transparent + public func componentwiseDividedReportingOverflow(by rhs: Complex) -> (partialValue: Complex, overflow: Bool) { + let real = self.real.dividedReportingOverflow(by: rhs.real) + let imaginary = self.imaginary.dividedReportingOverflow(by: rhs.imaginary) + + return (partialValue: Complex(real: real.partialValue, imaginary: imaginary.partialValue), overflow: real.overflow || imaginary.overflow) + } + + @_transparent + public func componentwiseDividingFullWidth(_ dividend: (high: Complex, low: Complex)) -> (quotient: Complex, remainder: Complex) { + let real = self.real.dividingFullWidth((high: dividend.high.real, low: dividend.low.real)) + let imaginary = self.imaginary.dividingFullWidth((high: dividend.high.imaginary, low: dividend.low.imaginary)) + + return (quotient: Complex(real: real.quotient, imaginary: imaginary.quotient), remainder: Complex(real: real.remainder, imaginary: imaginary.remainder)) + } +} diff --git a/Tests/ComplexTests/ComplexArithmeticTests.swift b/Tests/ComplexTests/ComplexArithmeticTests.swift index f8d2253..4f5c704 100644 --- a/Tests/ComplexTests/ComplexArithmeticTests.swift +++ b/Tests/ComplexTests/ComplexArithmeticTests.swift @@ -109,6 +109,19 @@ class ComplexArithmeticTests: XCTestCase { testAdditionIgnoringOverflow(forType: UInt.self) } + func testOverflowingAddition() { + testOverflowingAddition(forType: Int8.self) + testOverflowingAddition(forType: Int16.self) + testOverflowingAddition(forType: Int32.self) + testOverflowingAddition(forType: Int64.self) + testOverflowingAddition(forType: Int.self) + testOverflowingAddition(forType: UInt8.self) + testOverflowingAddition(forType: UInt16.self) + testOverflowingAddition(forType: UInt32.self) + testOverflowingAddition(forType: UInt64.self) + testOverflowingAddition(forType: UInt.self) + } + func testSubtraction() { testSubtraction(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: 2, imaginary: 2)) testSubtraction(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: 2, imaginary: 2)) @@ -154,6 +167,19 @@ class ComplexArithmeticTests: XCTestCase { testSubtractionIgnoringOverflow(forType: UInt.self) } + func testOverflowingSubtraction() { + testOverflowingSubtraction(forType: Int8.self) + testOverflowingSubtraction(forType: Int16.self) + testOverflowingSubtraction(forType: Int32.self) + testOverflowingSubtraction(forType: Int64.self) + testOverflowingSubtraction(forType: Int.self) + testOverflowingSubtraction(forType: UInt8.self) + testOverflowingSubtraction(forType: UInt16.self) + testOverflowingSubtraction(forType: UInt32.self) + testOverflowingSubtraction(forType: UInt64.self) + testOverflowingSubtraction(forType: UInt.self) + } + func testMultiplication() { testMultiplication(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: -5, imaginary: 10)) testMultiplication(Complex(real: 3, imaginary: 4), Complex(real: 1, imaginary: 2), Complex(real: -5, imaginary: 10)) @@ -216,6 +242,19 @@ class ComplexArithmeticTests: XCTestCase { testComponentwiseMultiplication(Complex(real: 3.0, imaginary: 4.0), Complex(real: 1.0, imaginary: 2.0)) } + func testComponentwiseOverflowingMultiplication() { + testComponentwiseOverflowingMultiplication(forType: Int8.self) + testComponentwiseOverflowingMultiplication(forType: Int16.self) + testComponentwiseOverflowingMultiplication(forType: Int32.self) + testComponentwiseOverflowingMultiplication(forType: Int64.self) + testComponentwiseOverflowingMultiplication(forType: Int.self) + testComponentwiseOverflowingMultiplication(forType: UInt8.self) + testComponentwiseOverflowingMultiplication(forType: UInt16.self) + testComponentwiseOverflowingMultiplication(forType: UInt32.self) + testComponentwiseOverflowingMultiplication(forType: UInt64.self) + testComponentwiseOverflowingMultiplication(forType: UInt.self) + } + func testDivision() { testDivision(Complex(real: 3, imaginary: 4), Complex(real: 2, imaginary: 1), Complex(real: 2, imaginary: 1)) testDivision(Complex(real: 3, imaginary: 4), Complex(real: 2, imaginary: 1), Complex(real: 2, imaginary: 1)) @@ -265,6 +304,19 @@ class ComplexArithmeticTests: XCTestCase { testComponentwiseDivision(Complex(real: 3.0, imaginary: 4.0), Complex(real: 1.0, imaginary: 2.0)) } + func testComponentwiseOverflowingDivision() { + testComponentwiseOverflowingDivision(forType: Int8.self) + testComponentwiseOverflowingDivision(forType: Int16.self) + testComponentwiseOverflowingDivision(forType: Int32.self) + testComponentwiseOverflowingDivision(forType: Int64.self) + testComponentwiseOverflowingDivision(forType: Int.self) + testComponentwiseOverflowingDivision(forType: UInt8.self) + testComponentwiseOverflowingDivision(forType: UInt16.self) + testComponentwiseOverflowingDivision(forType: UInt32.self) + testComponentwiseOverflowingDivision(forType: UInt64.self) + testComponentwiseOverflowingDivision(forType: UInt.self) + } + // MARK: Private Methods private func testAdditionWithZero(_ complex: Complex, file: StaticString = #file, line: UInt = #line) { @@ -408,6 +460,58 @@ class ComplexArithmeticTests: XCTestCase { } } + private func testOverflowingAddition(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: UnsignedInteger { + var lhs = Complex(real: Scalar.max, imaginary: 0) + var rhs = Complex(real: Scalar.max, imaginary: 0) + var result = lhs.addingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, Scalar.max - 1, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, 0, file: file, line: line) + + lhs = Complex(real: 0, imaginary: Scalar.max) + rhs = Complex(real: 0, imaginary: Scalar.max) + result = lhs.addingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, 0, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, Scalar.max - 1, file: file, line: line) + + lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + rhs = Complex(real: Scalar.max, imaginary: Scalar.max) + result = lhs.addingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, Scalar.max - 1, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, Scalar.max - 1, file: file, line: line) + } + + private func testOverflowingAddition(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: SignedInteger { + var lhs = Complex(real: Scalar.max, imaginary: 0) + var rhs = Complex(real: Scalar.max, imaginary: 0) + var result = lhs.addingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, -2, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, 0, file: file, line: line) + + lhs = Complex(real: 0, imaginary: Scalar.max) + rhs = Complex(real: 0, imaginary: Scalar.max) + result = lhs.addingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, 0, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, -2, file: file, line: line) + + lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + rhs = Complex(real: Scalar.max, imaginary: Scalar.max) + result = lhs.addingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, -2, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, -2, file: file, line: line) + } + private func testSubtraction(_ lhs: Complex, _ rhs: Complex, _ result: Complex, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(lhs - rhs, result, file: file, line: line) XCTAssertEqual(lhs .- rhs, result, file: file, line: line) @@ -502,6 +606,32 @@ class ComplexArithmeticTests: XCTestCase { } } + private func testOverflowingSubtraction(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger { + var lhs = Complex(real: Scalar.min, imaginary: 0) + var rhs = Complex(real: Scalar.max, imaginary: 0) + var result = lhs.subtractingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, 1, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, 0, file: file, line: line) + + lhs = Complex(real: 0, imaginary: Scalar.min) + rhs = Complex(real: 0, imaginary: Scalar.max) + result = lhs.subtractingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, 0, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, 1, file: file, line: line) + + lhs = Complex(real: Scalar.min, imaginary: Scalar.min) + rhs = Complex(real: Scalar.max, imaginary: Scalar.max) + result = lhs.subtractingReportingOverflow(rhs) + + XCTAssertTrue(result.overflow, file: file, line: line) + XCTAssertEqual(result.partialValue.real, 1, file: file, line: line) + XCTAssertEqual(result.partialValue.imaginary, 1, file: file, line: line) + } + private func testMultiplication(_ lhs: Complex, _ rhs: Complex, _ result: Complex, file: StaticString = #file, line: UInt = #line) { XCTAssertEqual(lhs * rhs, result, file: file, line: line) XCTAssertEqual(rhs * lhs, result, file: file, line: line) @@ -565,6 +695,70 @@ class ComplexArithmeticTests: XCTestCase { XCTAssertEqual(result.imaginary, lhs.imaginary * rhs.imaginary, file: file, line: line) } + private func testComponentwiseOverflowingMultiplication(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: UnsignedInteger { + var lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + var rhs = Complex(real: 2, imaginary: 2) + + let fullWidth = lhs.componentwiseMultipliedFullWidth(by: rhs) + XCTAssertEqual(fullWidth.high.real, 1, file: file, line: line) + XCTAssertEqual(fullWidth.high.imaginary, 1, file: file, line: line) + XCTAssertEqual(fullWidth.low.real, Scalar.Magnitude.max - 1, file: file, line: line) + XCTAssertEqual(fullWidth.low.imaginary, Scalar.Magnitude.max - 1, file: file, line: line) + + var overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs) + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, Scalar.max - 1, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, Scalar.max - 1, file: file, line: line) + + lhs = Complex(real: Scalar.max, imaginary: 0) + rhs = Complex(real: 2, imaginary: 0) + overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs) + + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, Scalar.max - 1, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, 0, file: file, line: line) + + lhs = Complex(real: 0, imaginary: Scalar.max) + rhs = Complex(real: 0, imaginary: 2) + overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs) + + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, 0, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, Scalar.max - 1, file: file, line: line) + } + + private func testComponentwiseOverflowingMultiplication(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: SignedInteger { + var lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + var rhs = Complex(real: 4, imaginary: 4) + + let fullWidth = lhs.componentwiseMultipliedFullWidth(by: rhs) + XCTAssertEqual(fullWidth.high.real, 1, file: file, line: line) + XCTAssertEqual(fullWidth.high.imaginary, 1, file: file, line: line) + XCTAssertEqual(fullWidth.low.real, Scalar.Magnitude.max - 3, file: file, line: line) + XCTAssertEqual(fullWidth.low.imaginary, Scalar.Magnitude.max - 3, file: file, line: line) + + var overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs) + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, -4, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, -4, file: file, line: line) + + lhs = Complex(real: Scalar.max, imaginary: 0) + rhs = Complex(real: 4, imaginary: 0) + overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs) + + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, -4, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, 0, file: file, line: line) + + lhs = Complex(real: 0, imaginary: Scalar.max) + rhs = Complex(real: 0, imaginary: 4) + overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs) + + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, 0, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, -4, file: file, line: line) + } + private func testDivision(_ lhs: Complex, _ rhs: Complex, _ result: Complex, file: StaticString = #file, line: UInt = #line) where Scalar: BinaryInteger { XCTAssertEqual(lhs / rhs, result, file: file, line: line) @@ -628,4 +822,31 @@ class ComplexArithmeticTests: XCTestCase { XCTAssertEqual(result.real, lhs.real / rhs.real, file: file, line: line) XCTAssertEqual(result.imaginary, lhs.imaginary / rhs.imaginary, file: file, line: line) } + + private func testComponentwiseOverflowingDivision(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger { + let lhs = Complex(real: Scalar.max, imaginary: Scalar.max) + var rhs = Complex(real: 0, imaginary: 0) + + let fullWidth = lhs.componentwiseDividingFullWidth((high: Complex(real: 1, imaginary: 1), low: Complex.zero)) + XCTAssertEqual(fullWidth.quotient.real, Scalar.isSigned ? 2 : 1, file: file, line: line) + XCTAssertEqual(fullWidth.quotient.imaginary, Scalar.isSigned ? 2 : 1, file: file, line: line) + XCTAssertEqual(fullWidth.remainder.real, Scalar.isSigned ? 2 : 1, file: file, line: line) + XCTAssertEqual(fullWidth.remainder.imaginary, Scalar.isSigned ? 2 : 1, file: file, line: line) + + var overflow = lhs.componentwiseDividedReportingOverflow(by: rhs) + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue, lhs, file: file, line: line) + + rhs = Complex(real: 0, imaginary: Scalar.max) + overflow = lhs.componentwiseDividedReportingOverflow(by: rhs) + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, lhs.real, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, 1, file: file, line: line) + + rhs = Complex(real: Scalar.max, imaginary: 0) + overflow = lhs.componentwiseDividedReportingOverflow(by: rhs) + XCTAssertTrue(overflow.overflow, file: file, line: line) + XCTAssertEqual(overflow.partialValue.real, 1, file: file, line: line) + XCTAssertEqual(overflow.partialValue.imaginary, lhs.imaginary, file: file, line: line) + } }