Skip to content

Commit d89e954

Browse files
author
Joe Newton
committed
Added trivial overflowing arithmetic functions for Complex where the scalar type conforms to FixedWidthInteger
1 parent 485927e commit d89e954

File tree

2 files changed

+280
-0
lines changed

2 files changed

+280
-0
lines changed

Sources/Complex/ComplexArithmetic.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,27 @@ extension Complex where Scalar: FixedWidthInteger {
184184
}
185185
}
186186

187+
extension Complex where Scalar: FixedWidthInteger {
188+
189+
@_transparent
190+
public func addingReportingOverflow(_ rhs: Complex<Scalar>) -> (partialValue: Complex<Scalar>, overflow: Bool) {
191+
let real = self.real.addingReportingOverflow(rhs.real)
192+
let imaginary = self.imaginary.addingReportingOverflow(rhs.imaginary)
193+
let overflow = real.overflow || imaginary.overflow
194+
195+
return (partialValue: Complex<Scalar>(real: real.partialValue, imaginary: imaginary.partialValue), overflow: overflow)
196+
}
197+
198+
@_transparent
199+
public func subtractingReportingOverflow(_ rhs: Complex<Scalar>) -> (partialValue: Complex<Scalar>, overflow: Bool) {
200+
let real = self.real.subtractingReportingOverflow(rhs.real)
201+
let imaginary = self.imaginary.subtractingReportingOverflow(rhs.imaginary)
202+
let overflow = real.overflow || imaginary.overflow
203+
204+
return (partialValue: Complex<Scalar>(real: real.partialValue, imaginary: imaginary.partialValue), overflow: overflow)
205+
}
206+
}
207+
187208
// MARK: - Multiplication
188209

189210
extension Complex {
@@ -276,6 +297,25 @@ extension Complex where Scalar: FixedWidthInteger {
276297
}
277298
}
278299

300+
extension Complex where Scalar: FixedWidthInteger {
301+
302+
@_transparent
303+
public func componentwiseMultipliedFullWidth(by rhs: Complex<Scalar>) -> (high: Complex<Scalar>, low: Complex<Scalar.Magnitude>) {
304+
let real = self.real.multipliedFullWidth(by: rhs.real)
305+
let imaginary = self.imaginary.multipliedFullWidth(by: rhs.imaginary)
306+
307+
return (high: Complex<Scalar>(real: real.high, imaginary: imaginary.high), low: Complex<Scalar.Magnitude>(real: real.low, imaginary: imaginary.low))
308+
}
309+
310+
@_transparent
311+
public func componentwiseMultipliedReportingOverflow(by rhs: Complex<Scalar>) -> (partialValue: Complex<Scalar>, overflow: Bool) {
312+
let real = self.real.multipliedReportingOverflow(by: rhs.real)
313+
let imaginary = self.imaginary.multipliedReportingOverflow(by: rhs.imaginary)
314+
315+
return (partialValue: Complex<Scalar>(real: real.partialValue, imaginary: imaginary.partialValue), overflow: real.overflow || imaginary.overflow)
316+
}
317+
}
318+
279319
// MARK: - Division (BinaryInteger)
280320

281321
extension Complex where Scalar: BinaryInteger {
@@ -375,3 +415,22 @@ extension Complex where Scalar: FloatingPoint {
375415
lhs = lhs ./ rhs
376416
}
377417
}
418+
419+
extension Complex where Scalar: FixedWidthInteger {
420+
421+
@_transparent
422+
public func componentwiseDividedReportingOverflow(by rhs: Complex<Scalar>) -> (partialValue: Complex<Scalar>, overflow: Bool) {
423+
let real = self.real.dividedReportingOverflow(by: rhs.real)
424+
let imaginary = self.imaginary.dividedReportingOverflow(by: rhs.imaginary)
425+
426+
return (partialValue: Complex<Scalar>(real: real.partialValue, imaginary: imaginary.partialValue), overflow: real.overflow || imaginary.overflow)
427+
}
428+
429+
@_transparent
430+
public func componentwiseDividingFullWidth(_ dividend: (high: Complex<Scalar>, low: Complex<Scalar.Magnitude>)) -> (quotient: Complex<Scalar>, remainder: Complex<Scalar>) {
431+
let real = self.real.dividingFullWidth((high: dividend.high.real, low: dividend.low.real))
432+
let imaginary = self.imaginary.dividingFullWidth((high: dividend.high.imaginary, low: dividend.low.imaginary))
433+
434+
return (quotient: Complex<Scalar>(real: real.quotient, imaginary: imaginary.quotient), remainder: Complex<Scalar>(real: real.remainder, imaginary: imaginary.remainder))
435+
}
436+
}

Tests/ComplexTests/ComplexArithmeticTests.swift

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ class ComplexArithmeticTests: XCTestCase {
109109
testAdditionIgnoringOverflow(forType: UInt.self)
110110
}
111111

112+
func testOverflowingAddition() {
113+
testOverflowingAddition(forType: Int8.self)
114+
testOverflowingAddition(forType: Int16.self)
115+
testOverflowingAddition(forType: Int32.self)
116+
testOverflowingAddition(forType: Int64.self)
117+
testOverflowingAddition(forType: Int.self)
118+
testOverflowingAddition(forType: UInt8.self)
119+
testOverflowingAddition(forType: UInt16.self)
120+
testOverflowingAddition(forType: UInt32.self)
121+
testOverflowingAddition(forType: UInt64.self)
122+
testOverflowingAddition(forType: UInt.self)
123+
}
124+
112125
func testSubtraction() {
113126
testSubtraction(Complex<Int8>(real: 3, imaginary: 4), Complex<Int8>(real: 1, imaginary: 2), Complex<Int8>(real: 2, imaginary: 2))
114127
testSubtraction(Complex<Int16>(real: 3, imaginary: 4), Complex<Int16>(real: 1, imaginary: 2), Complex<Int16>(real: 2, imaginary: 2))
@@ -154,6 +167,19 @@ class ComplexArithmeticTests: XCTestCase {
154167
testSubtractionIgnoringOverflow(forType: UInt.self)
155168
}
156169

170+
func testOverflowingSubtraction() {
171+
testOverflowingSubtraction(forType: Int8.self)
172+
testOverflowingSubtraction(forType: Int16.self)
173+
testOverflowingSubtraction(forType: Int32.self)
174+
testOverflowingSubtraction(forType: Int64.self)
175+
testOverflowingSubtraction(forType: Int.self)
176+
testOverflowingSubtraction(forType: UInt8.self)
177+
testOverflowingSubtraction(forType: UInt16.self)
178+
testOverflowingSubtraction(forType: UInt32.self)
179+
testOverflowingSubtraction(forType: UInt64.self)
180+
testOverflowingSubtraction(forType: UInt.self)
181+
}
182+
157183
func testMultiplication() {
158184
testMultiplication(Complex<Int8>(real: 3, imaginary: 4), Complex<Int8>(real: 1, imaginary: 2), Complex<Int8>(real: -5, imaginary: 10))
159185
testMultiplication(Complex<Int16>(real: 3, imaginary: 4), Complex<Int16>(real: 1, imaginary: 2), Complex<Int16>(real: -5, imaginary: 10))
@@ -216,6 +242,19 @@ class ComplexArithmeticTests: XCTestCase {
216242
testComponentwiseMultiplication(Complex<Float80>(real: 3.0, imaginary: 4.0), Complex<Float80>(real: 1.0, imaginary: 2.0))
217243
}
218244

245+
func testComponentwiseOverflowingMultiplication() {
246+
testComponentwiseOverflowingMultiplication(forType: Int8.self)
247+
testComponentwiseOverflowingMultiplication(forType: Int16.self)
248+
testComponentwiseOverflowingMultiplication(forType: Int32.self)
249+
testComponentwiseOverflowingMultiplication(forType: Int64.self)
250+
testComponentwiseOverflowingMultiplication(forType: Int.self)
251+
testComponentwiseOverflowingMultiplication(forType: UInt8.self)
252+
testComponentwiseOverflowingMultiplication(forType: UInt16.self)
253+
testComponentwiseOverflowingMultiplication(forType: UInt32.self)
254+
testComponentwiseOverflowingMultiplication(forType: UInt64.self)
255+
testComponentwiseOverflowingMultiplication(forType: UInt.self)
256+
}
257+
219258
func testDivision() {
220259
testDivision(Complex<Int8>(real: 3, imaginary: 4), Complex<Int8>(real: 2, imaginary: 1), Complex<Int8>(real: 2, imaginary: 1))
221260
testDivision(Complex<Int16>(real: 3, imaginary: 4), Complex<Int16>(real: 2, imaginary: 1), Complex<Int16>(real: 2, imaginary: 1))
@@ -265,6 +304,19 @@ class ComplexArithmeticTests: XCTestCase {
265304
testComponentwiseDivision(Complex<Float80>(real: 3.0, imaginary: 4.0), Complex<Float80>(real: 1.0, imaginary: 2.0))
266305
}
267306

307+
func testComponentwiseOverflowingDivision() {
308+
testComponentwiseOverflowingDivision(forType: Int8.self)
309+
testComponentwiseOverflowingDivision(forType: Int16.self)
310+
testComponentwiseOverflowingDivision(forType: Int32.self)
311+
testComponentwiseOverflowingDivision(forType: Int64.self)
312+
testComponentwiseOverflowingDivision(forType: Int.self)
313+
testComponentwiseOverflowingDivision(forType: UInt8.self)
314+
testComponentwiseOverflowingDivision(forType: UInt16.self)
315+
testComponentwiseOverflowingDivision(forType: UInt32.self)
316+
testComponentwiseOverflowingDivision(forType: UInt64.self)
317+
testComponentwiseOverflowingDivision(forType: UInt.self)
318+
}
319+
268320
// MARK: Private Methods
269321

270322
private func testAdditionWithZero<Scalar>(_ complex: Complex<Scalar>, file: StaticString = #file, line: UInt = #line) {
@@ -408,6 +460,58 @@ class ComplexArithmeticTests: XCTestCase {
408460
}
409461
}
410462

463+
private func testOverflowingAddition<Scalar>(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: UnsignedInteger {
464+
var lhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
465+
var rhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
466+
var result = lhs.addingReportingOverflow(rhs)
467+
468+
XCTAssertTrue(result.overflow, file: file, line: line)
469+
XCTAssertEqual(result.partialValue.real, Scalar.max - 1, file: file, line: line)
470+
XCTAssertEqual(result.partialValue.imaginary, 0, file: file, line: line)
471+
472+
lhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
473+
rhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
474+
result = lhs.addingReportingOverflow(rhs)
475+
476+
XCTAssertTrue(result.overflow, file: file, line: line)
477+
XCTAssertEqual(result.partialValue.real, 0, file: file, line: line)
478+
XCTAssertEqual(result.partialValue.imaginary, Scalar.max - 1, file: file, line: line)
479+
480+
lhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
481+
rhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
482+
result = lhs.addingReportingOverflow(rhs)
483+
484+
XCTAssertTrue(result.overflow, file: file, line: line)
485+
XCTAssertEqual(result.partialValue.real, Scalar.max - 1, file: file, line: line)
486+
XCTAssertEqual(result.partialValue.imaginary, Scalar.max - 1, file: file, line: line)
487+
}
488+
489+
private func testOverflowingAddition<Scalar>(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: SignedInteger {
490+
var lhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
491+
var rhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
492+
var result = lhs.addingReportingOverflow(rhs)
493+
494+
XCTAssertTrue(result.overflow, file: file, line: line)
495+
XCTAssertEqual(result.partialValue.real, -2, file: file, line: line)
496+
XCTAssertEqual(result.partialValue.imaginary, 0, file: file, line: line)
497+
498+
lhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
499+
rhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
500+
result = lhs.addingReportingOverflow(rhs)
501+
502+
XCTAssertTrue(result.overflow, file: file, line: line)
503+
XCTAssertEqual(result.partialValue.real, 0, file: file, line: line)
504+
XCTAssertEqual(result.partialValue.imaginary, -2, file: file, line: line)
505+
506+
lhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
507+
rhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
508+
result = lhs.addingReportingOverflow(rhs)
509+
510+
XCTAssertTrue(result.overflow, file: file, line: line)
511+
XCTAssertEqual(result.partialValue.real, -2, file: file, line: line)
512+
XCTAssertEqual(result.partialValue.imaginary, -2, file: file, line: line)
513+
}
514+
411515
private func testSubtraction<Scalar>(_ lhs: Complex<Scalar>, _ rhs: Complex<Scalar>, _ result: Complex<Scalar>, file: StaticString = #file, line: UInt = #line) {
412516
XCTAssertEqual(lhs - rhs, result, file: file, line: line)
413517
XCTAssertEqual(lhs .- rhs, result, file: file, line: line)
@@ -502,6 +606,32 @@ class ComplexArithmeticTests: XCTestCase {
502606
}
503607
}
504608

609+
private func testOverflowingSubtraction<Scalar>(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger {
610+
var lhs = Complex<Scalar>(real: Scalar.min, imaginary: 0)
611+
var rhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
612+
var result = lhs.subtractingReportingOverflow(rhs)
613+
614+
XCTAssertTrue(result.overflow, file: file, line: line)
615+
XCTAssertEqual(result.partialValue.real, 1, file: file, line: line)
616+
XCTAssertEqual(result.partialValue.imaginary, 0, file: file, line: line)
617+
618+
lhs = Complex<Scalar>(real: 0, imaginary: Scalar.min)
619+
rhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
620+
result = lhs.subtractingReportingOverflow(rhs)
621+
622+
XCTAssertTrue(result.overflow, file: file, line: line)
623+
XCTAssertEqual(result.partialValue.real, 0, file: file, line: line)
624+
XCTAssertEqual(result.partialValue.imaginary, 1, file: file, line: line)
625+
626+
lhs = Complex<Scalar>(real: Scalar.min, imaginary: Scalar.min)
627+
rhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
628+
result = lhs.subtractingReportingOverflow(rhs)
629+
630+
XCTAssertTrue(result.overflow, file: file, line: line)
631+
XCTAssertEqual(result.partialValue.real, 1, file: file, line: line)
632+
XCTAssertEqual(result.partialValue.imaginary, 1, file: file, line: line)
633+
}
634+
505635
private func testMultiplication<Scalar>(_ lhs: Complex<Scalar>, _ rhs: Complex<Scalar>, _ result: Complex<Scalar>, file: StaticString = #file, line: UInt = #line) {
506636
XCTAssertEqual(lhs * rhs, result, file: file, line: line)
507637
XCTAssertEqual(rhs * lhs, result, file: file, line: line)
@@ -565,6 +695,70 @@ class ComplexArithmeticTests: XCTestCase {
565695
XCTAssertEqual(result.imaginary, lhs.imaginary * rhs.imaginary, file: file, line: line)
566696
}
567697

698+
private func testComponentwiseOverflowingMultiplication<Scalar>(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: UnsignedInteger {
699+
var lhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
700+
var rhs = Complex<Scalar>(real: 2, imaginary: 2)
701+
702+
let fullWidth = lhs.componentwiseMultipliedFullWidth(by: rhs)
703+
XCTAssertEqual(fullWidth.high.real, 1, file: file, line: line)
704+
XCTAssertEqual(fullWidth.high.imaginary, 1, file: file, line: line)
705+
XCTAssertEqual(fullWidth.low.real, Scalar.Magnitude.max - 1, file: file, line: line)
706+
XCTAssertEqual(fullWidth.low.imaginary, Scalar.Magnitude.max - 1, file: file, line: line)
707+
708+
var overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs)
709+
XCTAssertTrue(overflow.overflow, file: file, line: line)
710+
XCTAssertEqual(overflow.partialValue.real, Scalar.max - 1, file: file, line: line)
711+
XCTAssertEqual(overflow.partialValue.imaginary, Scalar.max - 1, file: file, line: line)
712+
713+
lhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
714+
rhs = Complex<Scalar>(real: 2, imaginary: 0)
715+
overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs)
716+
717+
XCTAssertTrue(overflow.overflow, file: file, line: line)
718+
XCTAssertEqual(overflow.partialValue.real, Scalar.max - 1, file: file, line: line)
719+
XCTAssertEqual(overflow.partialValue.imaginary, 0, file: file, line: line)
720+
721+
lhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
722+
rhs = Complex<Scalar>(real: 0, imaginary: 2)
723+
overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs)
724+
725+
XCTAssertTrue(overflow.overflow, file: file, line: line)
726+
XCTAssertEqual(overflow.partialValue.real, 0, file: file, line: line)
727+
XCTAssertEqual(overflow.partialValue.imaginary, Scalar.max - 1, file: file, line: line)
728+
}
729+
730+
private func testComponentwiseOverflowingMultiplication<Scalar>(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger, Scalar: SignedInteger {
731+
var lhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
732+
var rhs = Complex<Scalar>(real: 4, imaginary: 4)
733+
734+
let fullWidth = lhs.componentwiseMultipliedFullWidth(by: rhs)
735+
XCTAssertEqual(fullWidth.high.real, 1, file: file, line: line)
736+
XCTAssertEqual(fullWidth.high.imaginary, 1, file: file, line: line)
737+
XCTAssertEqual(fullWidth.low.real, Scalar.Magnitude.max - 3, file: file, line: line)
738+
XCTAssertEqual(fullWidth.low.imaginary, Scalar.Magnitude.max - 3, file: file, line: line)
739+
740+
var overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs)
741+
XCTAssertTrue(overflow.overflow, file: file, line: line)
742+
XCTAssertEqual(overflow.partialValue.real, -4, file: file, line: line)
743+
XCTAssertEqual(overflow.partialValue.imaginary, -4, file: file, line: line)
744+
745+
lhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
746+
rhs = Complex<Scalar>(real: 4, imaginary: 0)
747+
overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs)
748+
749+
XCTAssertTrue(overflow.overflow, file: file, line: line)
750+
XCTAssertEqual(overflow.partialValue.real, -4, file: file, line: line)
751+
XCTAssertEqual(overflow.partialValue.imaginary, 0, file: file, line: line)
752+
753+
lhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
754+
rhs = Complex<Scalar>(real: 0, imaginary: 4)
755+
overflow = lhs.componentwiseMultipliedReportingOverflow(by: rhs)
756+
757+
XCTAssertTrue(overflow.overflow, file: file, line: line)
758+
XCTAssertEqual(overflow.partialValue.real, 0, file: file, line: line)
759+
XCTAssertEqual(overflow.partialValue.imaginary, -4, file: file, line: line)
760+
}
761+
568762
private func testDivision<Scalar>(_ lhs: Complex<Scalar>, _ rhs: Complex<Scalar>, _ result: Complex<Scalar>, file: StaticString = #file, line: UInt = #line) where Scalar: BinaryInteger {
569763
XCTAssertEqual(lhs / rhs, result, file: file, line: line)
570764

@@ -628,4 +822,31 @@ class ComplexArithmeticTests: XCTestCase {
628822
XCTAssertEqual(result.real, lhs.real / rhs.real, file: file, line: line)
629823
XCTAssertEqual(result.imaginary, lhs.imaginary / rhs.imaginary, file: file, line: line)
630824
}
825+
826+
private func testComponentwiseOverflowingDivision<Scalar>(forType: Scalar.Type, file: StaticString = #file, line: UInt = #line) where Scalar: FixedWidthInteger {
827+
let lhs = Complex<Scalar>(real: Scalar.max, imaginary: Scalar.max)
828+
var rhs = Complex<Scalar>(real: 0, imaginary: 0)
829+
830+
let fullWidth = lhs.componentwiseDividingFullWidth((high: Complex<Scalar>(real: 1, imaginary: 1), low: Complex<Scalar.Magnitude>.zero))
831+
XCTAssertEqual(fullWidth.quotient.real, Scalar.isSigned ? 2 : 1, file: file, line: line)
832+
XCTAssertEqual(fullWidth.quotient.imaginary, Scalar.isSigned ? 2 : 1, file: file, line: line)
833+
XCTAssertEqual(fullWidth.remainder.real, Scalar.isSigned ? 2 : 1, file: file, line: line)
834+
XCTAssertEqual(fullWidth.remainder.imaginary, Scalar.isSigned ? 2 : 1, file: file, line: line)
835+
836+
var overflow = lhs.componentwiseDividedReportingOverflow(by: rhs)
837+
XCTAssertTrue(overflow.overflow, file: file, line: line)
838+
XCTAssertEqual(overflow.partialValue, lhs, file: file, line: line)
839+
840+
rhs = Complex<Scalar>(real: 0, imaginary: Scalar.max)
841+
overflow = lhs.componentwiseDividedReportingOverflow(by: rhs)
842+
XCTAssertTrue(overflow.overflow, file: file, line: line)
843+
XCTAssertEqual(overflow.partialValue.real, lhs.real, file: file, line: line)
844+
XCTAssertEqual(overflow.partialValue.imaginary, 1, file: file, line: line)
845+
846+
rhs = Complex<Scalar>(real: Scalar.max, imaginary: 0)
847+
overflow = lhs.componentwiseDividedReportingOverflow(by: rhs)
848+
XCTAssertTrue(overflow.overflow, file: file, line: line)
849+
XCTAssertEqual(overflow.partialValue.real, 1, file: file, line: line)
850+
XCTAssertEqual(overflow.partialValue.imaginary, lhs.imaginary, file: file, line: line)
851+
}
631852
}

0 commit comments

Comments
 (0)