From 6ad64f1ce541ed6a95ce828956bd297ad78423b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Bystr=C3=B6m=20Ericsson?= Date: Thu, 22 Aug 2024 08:25:29 +0200 Subject: [PATCH] Add: Division/exactly() (#70). --- .../CoreKit/Models/Division+Rounding.swift | 6 ++- .../CoreKit/Models/Division+Validation.swift | 46 +++++++++++++++++++ .../Division+Validation.swift.swift | 43 +++++++++++++++++ .../BinaryInteger+Division.swift | 6 ++- 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 Sources/CoreKit/Models/Division+Validation.swift create mode 100644 Tests/CoreKitTests/Division+Validation.swift.swift diff --git a/Sources/CoreKit/Models/Division+Rounding.swift b/Sources/CoreKit/Models/Division+Rounding.swift index 715b8d84..b9792320 100644 --- a/Sources/CoreKit/Models/Division+Rounding.swift +++ b/Sources/CoreKit/Models/Division+Rounding.swift @@ -17,14 +17,14 @@ extension Division where Quotient: BinaryInteger, Remainder: BinaryInteger { // MARK: Transformations //=------------------------------------------------------------------------= - /// Increments the `quotient` if the `remainder` is positive. + /// Increments the `quotient` when the `remainder` is positive. @inlinable public consuming func ceil() -> Fallible { let instance: Self = consume self let increment: Quotient = instance.remainder > 0 ? 1 : 0 return instance.quotient.plus(increment) } - /// Decrements the `quotient` if the `remainder` is negative. + /// Decrements the `quotient` when the `remainder` is negative. @inlinable public consuming func floor() -> Fallible { let instance: Self = consume self let increment: Quotient = instance.remainder.isNegative ? 1 : 0 @@ -42,10 +42,12 @@ extension Fallible { // MARK: Transformations //=------------------------------------------------------------------------= + /// Increments the `quotient` when the `remainder` is positive. @inlinable public consuming func ceil() -> Fallible where Value == Division { self.value.ceil().veto(self.error) } + /// Decrements the `quotient` when the `remainder` is negative. @inlinable public consuming func floor() -> Fallible where Value == Division { self.value.floor().veto(self.error) } diff --git a/Sources/CoreKit/Models/Division+Validation.swift b/Sources/CoreKit/Models/Division+Validation.swift new file mode 100644 index 00000000..4c941d4f --- /dev/null +++ b/Sources/CoreKit/Models/Division+Validation.swift @@ -0,0 +1,46 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Ultimathnum open source project. +// +// Copyright (c) 2023 Oscar Byström Ericsson +// Licensed under Apache License, Version 2.0 +// +// See http://www.apache.org/licenses/LICENSE-2.0 for license information. +//=----------------------------------------------------------------------------= + +//*============================================================================* +// MARK: * Division x Validation +//*============================================================================* + +extension Division { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Returns the `quotient` and an `error` indicator. + /// + /// The `error` is set when the `remainder` is nonzero. + /// + @inlinable public consuming func exactly() -> Fallible { + self.quotient.veto(!self.remainder.isZero) + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Recoverable +//=----------------------------------------------------------------------------= + +extension Fallible { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Returns the `quotient` and an `error` indicator. + /// + /// The `error` is set when the `remainder` is nonzero. + /// + @inlinable public consuming func exactly() -> Fallible where Value == Division { + self.value.exactly().veto(self.error) + } +} diff --git a/Tests/CoreKitTests/Division+Validation.swift.swift b/Tests/CoreKitTests/Division+Validation.swift.swift new file mode 100644 index 00000000..53f9ece5 --- /dev/null +++ b/Tests/CoreKitTests/Division+Validation.swift.swift @@ -0,0 +1,43 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Ultimathnum open source project. +// +// Copyright (c) 2023 Oscar Byström Ericsson +// Licensed under Apache License, Version 2.0 +// +// See http://www.apache.org/licenses/LICENSE-2.0 for license information. +//=----------------------------------------------------------------------------= + +import CoreKit +import TestKit + +//*============================================================================* +// MARK: * Division x Validation +//*============================================================================* + +extension DivisionTests { + + //=------------------------------------------------------------------------= + // MARK: Tests + //=------------------------------------------------------------------------= + + func testExactly() { + func whereIs(_ quotient: Q.Type, _ remainder: R.Type) where Q: SystemsInteger, R: SystemsInteger { + let patterns = I8(-2)...I8(2) + for quotient in patterns.lazy.map(Q.init(load:)) { + for remainder in patterns.lazy.map(R.init(load:)) { + let error = !remainder.isZero + let division = Division(quotient: quotient, remainder: remainder) + Test().same(division .exactly(), Fallible(quotient, error: error)) + Test().same(division.veto(false).exactly(), Fallible(quotient, error: error)) + Test().same(division.veto(true ).exactly(), Fallible(quotient, error: true )) + } + } + } + + for quotient in coreSystemsIntegers { + for remainder in coreSystemsIntegers { + whereIs(quotient, remainder) + } + } + } +} diff --git a/Tests/UltimathnumTests/BinaryInteger+Division.swift b/Tests/UltimathnumTests/BinaryInteger+Division.swift index 1dd47293..ffb5e917 100644 --- a/Tests/UltimathnumTests/BinaryInteger+Division.swift +++ b/Tests/UltimathnumTests/BinaryInteger+Division.swift @@ -658,9 +658,13 @@ extension BinaryIntegerTestsOnDivision { let floor: Fallible = division.floor() success &+= IX(Bit(division.veto(false).floor() == floor)) success &+= IX(Bit(division.veto(true ).floor() == floor.veto())) + + let exactly: Fallible = division.exactly() + success &+= IX(Bit(division.veto(false).exactly() == exactly)) + success &+= IX(Bit(division.veto(true ).exactly() == exactly.veto())) } - Test().same(success, rounds &* 10) + Test().same(success, rounds &* 12) } func whereIsUnsignedSystemsInteger(_ type: T.Type, rounds: IX, randomness: consuming FuzzerInt) where T: SystemsInteger & UnsignedInteger {