From 770c0d4f17aa2a0bca8df2bc5dd2310778cdda02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Bystr=C3=B6m=20Ericsson?= Date: Mon, 16 Dec 2024 07:27:34 +0100 Subject: [PATCH] The magic `Divider` constants README.md section (#114). This patch also introduces a 2-by-1 convenience method to natural integers. --- README.md | 25 +++++++++++++ Sources/CoreKit/BinaryInteger+Division.swift | 17 +++++++++ Sources/CoreKit/BinaryInteger+Systems.swift | 7 +++- .../BinaryInteger+Division.swift.swift | 37 +++++++++++++++++++ .../Divider+Documentation.swift | 36 +++++++++++++++--- 5 files changed, 115 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c601df6d..c68ce472 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [The overpowered `BinaryInteger`](#overview-the-overpowered-binary-integer) - [The `Fallible` redemption arc](#overview-the-fallible-redemption-arc) - [The precondition `Guarantee` types](#overview-the-precondition-guarantee-types) + - [The magic `Divider` constants](#overview-the-magic-divider-constants) @@ -272,3 +273,27 @@ init(_:) // error: precondition init(unchecked:) // error: assert init(unsafe:) // error: %%%%%% ``` + + + +### The magic `Divider` constants + +You know how the compiler sometimes replaces division with multiplication, right? Great, now it is your turn to be a wizard! `Divider` and `Divider21` find magic constants that let us perform divisions using multiplication, addition, and shifts. Note that the latter divides numbers twice the size of the divisor. + +```swift +let random = U8.random() +let divisor = Nonzero(U8.random(in: 1...255)) +let divider = Divider(divisor) +let typical = random.division(divisor) as Division // div +let magical = random.division(divider) as Division // mul-add-shr +precondition(typical == magical) // quotient and remainder +``` + +```swift +let random = Doublet(low: U8.random(), high: U8.random()) +let divisor = Nonzero(U8.random(in: 1...255)) +let divider = Divider21(divisor) +let typical = U8.division(random, by: divisor) as Fallible> +let magical = U8.division(random, by: divider) as Fallible> +precondition(typical == magical) // quotient, remainder, and error indicator +``` diff --git a/Sources/CoreKit/BinaryInteger+Division.swift b/Sources/CoreKit/BinaryInteger+Division.swift index c24eb302..3297df2c 100644 --- a/Sources/CoreKit/BinaryInteger+Division.swift +++ b/Sources/CoreKit/BinaryInteger+Division.swift @@ -405,6 +405,23 @@ extension BinaryInteger where Self: SystemsInteger & UnsignedInteger { @inlinable public consuming func division(_ divider: borrowing Divider) -> Division { divider.division(dividing: self) } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Divider x 2-by-1 + //=------------------------------------------------------------------------= + + /// Returns the `quotient`, `remainder` and `error` of dividing the `dividend` by the `divider`. + /// + /// ### Division of 2 by 1 + /// + /// - Note: The `error` is set if the operation is `lossy`. + /// + @inlinable public static func division( + _ dividend: consuming Doublet, + by divider: borrowing Divider21 + ) -> Fallible> { + divider.division(dividing: dividend) + } } //=----------------------------------------------------------------------------= diff --git a/Sources/CoreKit/BinaryInteger+Systems.swift b/Sources/CoreKit/BinaryInteger+Systems.swift index 7c25acdd..d77fb869 100644 --- a/Sources/CoreKit/BinaryInteger+Systems.swift +++ b/Sources/CoreKit/BinaryInteger+Systems.swift @@ -38,6 +38,11 @@ public protocol SystemsInteger: EdgyInteger, FiniteInteger where Mag /// Returns the byte swapped version of `self`. @inlinable consuming func reversed(_ type: U8.Type) -> Self - /// Returns the result of dividing the `dividend` by the `divisor`. + /// Returns the `quotient`, `remainder` and `error` of dividing the `dividend` by the `divisor`. + /// + /// ### Division of 2 by 1 + /// + /// - Note: The `error` is set if the operation is `lossy`. + /// @inlinable static func division(_ dividend: consuming Doublet, by divisor: borrowing Nonzero) -> Fallible> } diff --git a/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift b/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift index 9eb356e0..45c289ca 100644 --- a/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift +++ b/Tests/UltimathnumTests/BinaryInteger+Division.swift.swift @@ -967,6 +967,43 @@ import TestKit } } +//*============================================================================* +// MARK: * Binary Integer x Division x Conveniences x 2 by 1 +//*============================================================================* + +@Suite struct BinaryIntegerTestsOnDivisionConveniences21 { + + //=------------------------------------------------------------------------= + // MARK: Tests x Natural + //=------------------------------------------------------------------------= + + @Test( + "BinaryInteger/division/conveniences/2-by-1: as NaturalInteger", + Tag.List.tags(.forwarding, .generic, .random), + arguments: typesAsSystemsIntegerAsUnsigned, fuzzers + ) func asNaturalInteger( + type: any SystemsIntegerAsUnsigned.Type, randomness: consuming FuzzerInt + ) throws { + + try whereIs(type) + func whereIs(_ type: T.Type) throws where T: SystemsIntegerAsUnsigned { + for _ in 0 ..< 32 { + let low = T.entropic(using: &randomness) + let high = T.entropic(using: &randomness) + let dividend = Doublet(low: low, high: high) + let divisor = T.entropic(using: &randomness) + + guard let divisor = Nonzero(exactly: divisor) else { continue } + let divider = Divider21(divisor) as Divider21 as Divider21 as Divider21 + + let typical: Fallible = T.division(dividend, by: divisor) + let magical: Fallible = T.division(dividend, by: divider) + try #require(typical == magical) + } + } + } +} + //*============================================================================* // MARK: * Binary Integer x Division x Open Source Issues //*============================================================================* diff --git a/Tests/UltimathnumTests/Divider+Documentation.swift b/Tests/UltimathnumTests/Divider+Documentation.swift index 2e5091db..340b9b73 100644 --- a/Tests/UltimathnumTests/Divider+Documentation.swift +++ b/Tests/UltimathnumTests/Divider+Documentation.swift @@ -21,11 +21,35 @@ import TestKit //=------------------------------------------------------------------------= @Test("README") func readme() { - let random = U8.random() - let divisor = Nonzero(U8.random(in: 1...255)) - let divider = Divider(divisor.value) - let typical = random.division(divisor) as Division // div - let magical = random.division(divider) as Division // mul-add-shr - precondition(typical == magical) // quotient and remainder + for _ in 0 ..< 8 { + let random = U8.random() + let divisor = Nonzero(U8.random(in: 1...255)) + let divider = Divider(divisor) + let typical = random.division(divisor) as Division // div + let magical = random.division(divider) as Division // mul-add-shr + precondition(typical == magical) // quotient and remainder + } + } +} + +//*============================================================================* +// MARK: * Divider x Documentation x 2-by-1 +//*============================================================================* + +@Suite struct DividerTestsOnDocumentation21 { + + //=------------------------------------------------------------------------= + // MARK: Tests + //=------------------------------------------------------------------------= + + @Test("README") func readme() { + for _ in 0 ..< 8 { + let random = Doublet(low: U8.random(), high: U8.random()) + let divisor = Nonzero(U8.random(in: 1...255)) + let divider = Divider21(divisor) + let typical = U8.division(random, by: divisor) as Fallible> + let magical = U8.division(random, by: divider) as Fallible> + precondition(typical == magical) // quotient, remainder, and error indicator + } } }