From ac40ed180826256ac3f4d48ad477d741877e68c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Bystr=C3=B6m=20Ericsson?= Date: Wed, 20 Sep 2023 14:04:34 +0200 Subject: [PATCH] [NBKCoreKit] StrictUnsignedInteger (#83). --- Sources/NBKCoreKit/Private/NBK+Division.swift | 13 +- .../Private/NBK+Limbs+Addition.swift | 256 --------------- .../Private/NBK+Limbs+Multiplication.swift | 37 --- .../Private/NBK+Limbs+Subtraction.swift | 305 ----------------- .../NBKStrictUnsignedInteger+Addition.swift | 260 +++++++++++++++ ...NBKStrictUnsignedInteger+Complements.swift | 47 +++ .../NBKStrictUnsignedInteger+Division.swift | 64 ++++ ...StrictUnsignedInteger+Multiplication.swift | 61 ++++ .../NBKStrictUnsignedInteger+Shifts.swift | 157 +++++++++ ...NBKStrictUnsignedInteger+Subtraction.swift | 308 ++++++++++++++++++ .../Private/NBKStrictUnsignedInteger.swift | 124 +++++++ .../NBKDoubleWidth+Multiplication+Digit.swift | 2 +- ...> NBKStrictUnsignedInteger+Addition.swift} | 70 ++-- ...> NBKStrictUnsignedInteger+Division.swift} | 34 +- ...trictUnsignedInteger+Multiplication.swift} | 32 +- ...BKStrictUnsignedInteger+Subtraction.swift} | 76 +++-- 16 files changed, 1154 insertions(+), 692 deletions(-) delete mode 100644 Sources/NBKCoreKit/Private/NBK+Limbs+Addition.swift delete mode 100644 Sources/NBKCoreKit/Private/NBK+Limbs+Multiplication.swift delete mode 100644 Sources/NBKCoreKit/Private/NBK+Limbs+Subtraction.swift create mode 100644 Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Addition.swift create mode 100644 Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Complements.swift create mode 100644 Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Division.swift create mode 100644 Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Multiplication.swift create mode 100644 Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Shifts.swift create mode 100644 Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Subtraction.swift create mode 100644 Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger.swift rename Tests/NBKCoreKitTests/Private/{NBK+Limbs+Addition.swift => NBKStrictUnsignedInteger+Addition.swift} (72%) rename Tests/NBKCoreKitTests/Private/{NBK+Limbs+Division.swift => NBKStrictUnsignedInteger+Division.swift} (65%) rename Tests/NBKCoreKitTests/Private/{NBK+Limbs+Multiplication.swift => NBKStrictUnsignedInteger+Multiplication.swift} (60%) rename Tests/NBKCoreKitTests/Private/{NBK+Limbs+Subtraction.swift => NBKStrictUnsignedInteger+Subtraction.swift} (82%) diff --git a/Sources/NBKCoreKit/Private/NBK+Division.swift b/Sources/NBKCoreKit/Private/NBK+Division.swift index 02a35b2b..81acc2c2 100644 --- a/Sources/NBKCoreKit/Private/NBK+Division.swift +++ b/Sources/NBKCoreKit/Private/NBK+Division.swift @@ -77,9 +77,18 @@ extension NBK { return PVO(dividend._lowWord & (divisor &- 1), false) } //=--------------------------------------= + if divisor.isZero { + return PVO(dividend._lowWord, true) + } + //=--------------------------------------= let minus = T.isSigned && dividend < T.zero - let pvo = NBK.remainderReportingOverflowAsLenientUnsignedInteger(of: dividend.magnitude.words, dividingBy: divisor) - return PVO((minus && !pvo.partialValue.isZero) ? (divisor &- pvo.partialValue) : pvo.partialValue, pvo.overflow) + var remainder = 000000000000000000 as UInt + + for word in dividend.magnitude.words.reversed() { + remainder = divisor.dividingFullWidth(HL(high: remainder, low: word)).remainder + } + + return PVO((minus && !remainder.isZero) ? (divisor &- remainder) : remainder, false) } /// Returns the least positive `residue` of dividing the `dividend` by `source.bitWidth`. diff --git a/Sources/NBKCoreKit/Private/NBK+Limbs+Addition.swift b/Sources/NBKCoreKit/Private/NBK+Limbs+Addition.swift deleted file mode 100644 index 91deaa22..00000000 --- a/Sources/NBKCoreKit/Private/NBK+Limbs+Addition.swift +++ /dev/null @@ -1,256 +0,0 @@ -//=----------------------------------------------------------------------------= -// This source file is part of the Numberick 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. -//=----------------------------------------------------------------------------= - -//=----------------------------------------------------------------------------= -// TODO: See if consuming arguments removes the need for inout in Swift 5.9 -//*============================================================================* -// MARK: * NBK x Limbs x Addition x Limbs + Bit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Increments `pointee` by the sum of `limbs` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by limbs: some Collection, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.incrementSufficientUnsignedInteger(&pointee, by: limbs, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - /// Partially increments `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - This operation does not continue beyond the operand intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by limbs: some Collection, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.incrementSufficientUnsignedIntegerInIntersection(&pointee, by: limbs, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Increments `pointee` by the sum of `limbs` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by limbs: some Collection, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - NBK.incrementSufficientUnsignedIntegerInIntersection(&pointee, by: limbs, plus: &bit, at: &index) - NBK.incrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - } - - /// Partially increments `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - This operation does not continue beyond the operand intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by limbs: some Collection, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - for limbsIndex in limbs.indices { - NBK.incrementSufficientUnsignedIntegerInIntersection(&pointee, by: limbs[limbsIndex], plus: &bit, at: &index) - } - } -} - -//*============================================================================* -// MARK: * NBK x Limbs x Addition x Digit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Increments `pointee` by `digit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool - bit = NBK.incrementSufficientUnsignedInteger(&pointee, by: digit, at: &index) - return IO(index as T.Index, overflow: bit as Bool) - } - - /// Partially increments `pointee` by `digit` at `index`. - /// - /// - This operation does not continue beyond the operand intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func incrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool - bit = NBK.incrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, at: &index) - return IO(index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Increments `pointee` by `digit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, at index: inout T.Index) -> Bool - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var bit = NBK.incrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, at: &index) - NBK.incrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - return bit as Bool - } - - /// Partially increments `pointee` by `digit` at `index`. - /// - /// - This operation does not continue beyond the operand intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, at index: inout T.Index) -> Bool - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - defer{ pointee.formIndex(after: &index) } - return pointee[index].addReportingOverflow(digit) - } -} - -//*============================================================================* -// MARK: * NBK x Limbs x Addition x Digit + Bit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Increments `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.incrementSufficientUnsignedInteger(&pointee, by: digit, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - /// Partially increments `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - This operation does not continue beyond the operand intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func incrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit, digit: T.Element = digit - NBK.incrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Increments `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - NBK.incrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, plus: &bit, at: &index) - NBK.incrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - } - - /// Partially increments `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - This operation does not continue beyond the operand intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var digit: T.Element = digit - - if bit { - bit = digit.addReportingOverflow(1 as T.Element.Digit) - } - - if !bit { - bit = pointee[index].addReportingOverflow(digit) - } - - pointee.formIndex(after: &index) - } -} - -//*============================================================================* -// MARK: * NBK x Limbs x Addition x Bit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Increments `pointee` by `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.incrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformationsx x Inout - //=------------------------------------------------------------------------= - - /// Increments `pointee` by `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func incrementSufficientUnsignedInteger( - _ pointee: inout T, by bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - while bit && index < pointee.endIndex { - bit = pointee[index].addReportingOverflow(1 as T.Element.Digit) - pointee.formIndex(after: &index) - } - } -} diff --git a/Sources/NBKCoreKit/Private/NBK+Limbs+Multiplication.swift b/Sources/NBKCoreKit/Private/NBK+Limbs+Multiplication.swift deleted file mode 100644 index bfcdd0f2..00000000 --- a/Sources/NBKCoreKit/Private/NBK+Limbs+Multiplication.swift +++ /dev/null @@ -1,37 +0,0 @@ -//=----------------------------------------------------------------------------= -// This source file is part of the Numberick 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: * NBK x Limbs x Multiplication x Digit x Unsigned -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations x Full Width - //=------------------------------------------------------------------------= - - /// Multiplies `limbs` by `multiplicand` and adds `addend`. - /// - /// - Note: In the case where `limbs` is empty, the `addend` is returned. - /// - @inlinable public static func multiplyFullWidthLenientUnsignedInteger( - _ limbs: inout T, by multiplicand: T.Element, add addend: T.Element) - -> T.Element where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var carry = addend - - for index in limbs.indices { - var subproduct = limbs[index].multipliedFullWidth(by: multiplicand) - subproduct.high &+= T.Element(bit: subproduct.low.addReportingOverflow(carry)) - (carry, limbs[index]) = subproduct as HL - } - - return carry as T.Element - } -} diff --git a/Sources/NBKCoreKit/Private/NBK+Limbs+Subtraction.swift b/Sources/NBKCoreKit/Private/NBK+Limbs+Subtraction.swift deleted file mode 100644 index c3bb39d1..00000000 --- a/Sources/NBKCoreKit/Private/NBK+Limbs+Subtraction.swift +++ /dev/null @@ -1,305 +0,0 @@ -//=----------------------------------------------------------------------------= -// This source file is part of the Numberick 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. -//=----------------------------------------------------------------------------= - -//=----------------------------------------------------------------------------= -// TODO: see if consuming the arguments removes the need for inout in Swift 5.9 -//*============================================================================* -// MARK: * NBK x Limbs x Subtraction x Limbs + Bit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by the sum of `limbs` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by limbs: some Collection, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.decrementSufficientUnsignedInteger(&pointee, by: limbs, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - /// Partially decrements `pointee` by the sum of `limbs` and `bit` at `index`. - /// - /// This operation does not continue beyond the `pointee` subsequence intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by limbs: some Collection, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: limbs, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by the sum of `limbs` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by limbs: some Collection, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: limbs, plus: &bit, at: &index) - NBK.decrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - } - - /// Partially decrements `pointee` by the sum of `limbs` and `bit` at `index`. - /// - /// This operation does not continue beyond the `pointee` subsequence intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by limbs: some Collection, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - for limbsIndex in limbs.indices { - NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: limbs[limbsIndex], plus: &bit, at: &index) - } - } -} - -//*============================================================================* -// MARK: * NBK x Limbs x Subtraction x Digit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by `digit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool - bit = NBK.decrementSufficientUnsignedInteger(&pointee, by: digit, at: &index) - return IO(index as T.Index, overflow: bit as Bool) - } - - /// Partially decrements `pointee` by `digit` at `index`. - /// - /// This operation does not continue beyond the `pointee` subsequence intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool - bit = NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, at: &index) - return IO(index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by `digit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, at index: inout T.Index) -> Bool - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var bit = NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, at: &index) - NBK.decrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - return bit as Bool - } - - /// Partially decrements `pointee` by `digit` at `index`. - /// - /// This operation does not continue beyond the `pointee` subsequence intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, at index: inout T.Index) -> Bool - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - defer{ pointee.formIndex(after: &index) } - return pointee[index].subtractReportingOverflow(digit) - } -} - -//*============================================================================* -// MARK: * NBK x Limbs x Subtraction x Digit + Bit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.decrementSufficientUnsignedInteger(&pointee, by: digit, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - /// Partially decrements `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// This operation does not continue beyond the `pointee` subsequence intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by digit: T.Element, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: digit, plus: &bit, at: &index) - NBK.decrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - } - - /// Partially decrements `pointee` by the sum of `digit` and `bit` at `index`. - /// - /// This operation does not continue beyond the `pointee` subsequence intersection. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedIntegerInIntersection( - _ pointee: inout T, by digit: T.Element, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var digit: T.Element = digit - - if bit { - bit = digit.addReportingOverflow(1 as T.Element.Digit) - } - - if !bit { - bit = pointee[index].subtractReportingOverflow(digit) - } - - pointee.formIndex(after: &index) - } -} - -//*============================================================================* -// MARK: * NBK x Limbs x Subtraction x Bit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.decrementSufficientUnsignedInteger(&pointee, by: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - while bit && index < pointee.endIndex { - bit = pointee[index].subtractReportingOverflow(1 as T.Element.Digit) - pointee.formIndex(after: &index) - } - } -} - -//*============================================================================* -// MARK: * NBK x Limbs x Subtraction x Limbs × Digit + Digit + Bit -//*============================================================================* - -extension NBK { - - //=------------------------------------------------------------------------= - // MARK: Transformations - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by the product of `limbs` and `multiplicand` at `index`, - /// and the sum of `subtrahend` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @_transparent @discardableResult public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by limbs: T, times multiplicand: T.Element, - plus subtrahend: T.Element, plus bit: Bool, at index: T.Index) -> IO - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var index: T.Index = index, bit: Bool = bit - NBK.decrementSufficientUnsignedInteger(&pointee, by: limbs, times: multiplicand, plus: subtrahend, plus: &bit, at: &index) - return IO(index: index as T.Index, overflow: bit as Bool) - } - - //=------------------------------------------------------------------------= - // MARK: Transformations x Inout - //=------------------------------------------------------------------------= - - /// Decrements `pointee` by the product of `limbs` and `multiplicand` at `index`, - /// and the sum of `subtrahend` and `bit` at `index`. - /// - /// - Returns: An overflow indicator and its index in `pointee`. - /// - @inlinable public static func decrementSufficientUnsignedInteger( - _ pointee: inout T, by limbs: T, times multiplicand: T.Element, - plus subtrahend: T.Element, plus bit: inout Bool, at index: inout T.Index) - where T: MutableCollection, T.Element: NBKFixedWidthInteger & NBKUnsignedInteger { - var last: T.Element = subtrahend - - for limbsIndex in limbs.indices { - var subproduct = limbs[limbsIndex].multipliedFullWidth(by: multiplicand) - last = T.Element(bit: subproduct.low.addReportingOverflow(last)) &+ subproduct.high - NBK.decrementSufficientUnsignedIntegerInIntersection(&pointee, by: subproduct.low, plus: &bit, at: &index) - } - - NBK.decrementSufficientUnsignedInteger(&pointee, by: last, plus: &bit, at: &index) - } -} diff --git a/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Addition.swift b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Addition.swift new file mode 100644 index 00000000..e7c55bf7 --- /dev/null +++ b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Addition.swift @@ -0,0 +1,260 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick 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. +//=----------------------------------------------------------------------------= +// TODO: See whether consuming arguments removes the need for inout in Swift 5.9 +//=----------------------------------------------------------------------------= + +//*============================================================================* +// MARK: * NBK x Strict Unsigned Integer x Addition +//*============================================================================* +//=----------------------------------------------------------------------------= +// MARK: + Bit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Increments `base` by `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func increment( + by bit: Bool, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.increment(by: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformationsx x Inout + //=------------------------------------------------------------------------= + + /// Increments `base` by `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func increment( + by bit: inout Bool, at index: inout Base.Index) { + while bit && index < self.storage.endIndex { + bit = self.storage[index].addReportingOverflow(1 as Base.Element.Digit) + self.storage.formIndex(after: &index) + } + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Digit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Increments `base` by `digit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func increment( + by digit: Base.Element, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool + //=--------------------------------------= + bit = self.increment(by: digit, at: &index) + //=--------------------------------------= + return NBK.IO(index as Base.Index, overflow: bit as Bool) + } + + /// Partially increments `base` by `digit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func incrementInIntersection( + by digit: Base.Element, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool + //=--------------------------------------= + bit = self.incrementInIntersection(by: digit, at: &index) + //=--------------------------------------= + return NBK.IO(index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Inout + //=------------------------------------------------------------------------= + + /// Increments `base` by `digit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func increment( + by digit: Base.Element, at index: inout Base.Index) -> Bool { + var bit = self.incrementInIntersection(by: digit, at: &index) + self.increment(by: &bit, at: &index); return bit as Bool + } + + /// Partially increments `base` by `digit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func incrementInIntersection( + by digit: Base.Element, at index: inout Base.Index) -> Bool { + defer{ self.storage.formIndex(after: &index) } + return self.storage[index].addReportingOverflow(digit) + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Digit + Bit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Increments `base` by the sum of `digit` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func increment( + by digit: Base.Element, plus bit: Bool, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.increment(by: digit, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + /// Partially increments `base` by the sum of `digit` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func incrementInIntersection( + by digit: Base.Element, plus bit: Bool, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit, digit: Base.Element = digit + //=--------------------------------------= + self.incrementInIntersection(by: digit, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformationsx x Inout + //=------------------------------------------------------------------------= + + /// Partially increments `base` by the sum of `digit` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func increment( + by digit: Base.Element, plus bit: inout Bool, at index: inout Base.Index) { + self.incrementInIntersection(by: digit, plus: &bit, at: &index) + self.increment(by: &bit, at: &index) + } + + /// Partially increments `base` by the sum of `digit` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func incrementInIntersection( + by digit: Base.Element, plus bit: inout Bool, at index: inout Base.Index) { + var digit: Base.Element = digit + //=--------------------------------------= + if bit { + bit = digit.addReportingOverflow(1 as Base.Element.Digit) + } + + if !bit { + bit = self.storage[index].addReportingOverflow(digit) + } + //=--------------------------------------= + self.storage.formIndex(after: &index) + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Elements + Bit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Increments `base` by the sum of `elements` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func increment( + by elements: some Collection, plus bit: Bool = false, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.increment(by: elements, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + /// Partially increments `base` by the sum of `elements` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func incrementInIntersection( + by elements: some Collection, plus bit: Bool = false, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.incrementInIntersection(by: elements, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Inout + //=------------------------------------------------------------------------= + + /// Increments `base` by the sum of `elements` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func increment( + by elements: some Collection, plus bit: inout Bool, at index: inout Base.Index) { + self.incrementInIntersection(by: elements, plus: &bit, at: &index) + self.increment(by: &bit, at: &index) + } + + /// Partially increments `base` by the sum of `elements` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func incrementInIntersection( + by elements: some Collection, plus bit: inout Bool, at index: inout Base.Index) { + for elementIndex in elements.indices { + self.incrementInIntersection(by: elements[elementIndex], plus: &bit, at: &index) + } + } +} diff --git a/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Complements.swift b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Complements.swift new file mode 100644 index 00000000..585ee71d --- /dev/null +++ b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Complements.swift @@ -0,0 +1,47 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick 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: * NBK x Strict Unsigned Integer x Complements +//*============================================================================* + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations x One's Complement + //=------------------------------------------------------------------------= + + @inlinable mutating func formOnesComplement() { + for index in self.storage.indices { + self.storage[index].formOnesComplement() + } + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Two's Complement + //=------------------------------------------------------------------------= + + @inlinable mutating func formTwosComplement() { + _ = self.formTwosComplementSubsequence(true) + } + + @inlinable mutating func formTwosComplementReportingOverflow() -> Bool { + self.formTwosComplementSubsequence(true) + } + + @inlinable mutating func formTwosComplementSubsequence(_ carry: Bool) -> Bool { + var carry = carry + + for index in self.storage.indices { + carry = self.storage[index].formTwosComplementSubsequence(carry) + } + + return carry as Bool + } +} diff --git a/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Division.swift b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Division.swift new file mode 100644 index 00000000..b6e78eda --- /dev/null +++ b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Division.swift @@ -0,0 +1,64 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick 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: * NBK x Strict Unsigned Integer x Division +//*============================================================================* +//=----------------------------------------------------------------------------= +// MARK: + Digit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Returns the `remainder` of dividing the `base` by the `divisor`, + /// along with an `overflow` indicator. + /// + /// - Note: In the case of `overflow`, the result is `base.first`. + /// + @inlinable public func remainderReportingOverflow( + dividingBy divisor: Base.Element) -> PVO { + //=--------------------------------------= + if divisor.isZero { + return PVO(partialValue: self.first, overflow: true) + } + //=--------------------------------------= + var remainder = 0 as Base.Element + + for index in self.storage.indices.reversed() { + remainder = divisor.dividingFullWidth(HL(high: remainder, low: self.storage[index])).remainder + } + + return PVO(partialValue: remainder, overflow: false) + } + + /// Forms the `quotient` of dividing the `base` by the `divisor`, + /// and returns the `remainder` along with an `overflow` indicator. + /// + /// - Note: In the case of `overflow`, the result is `base` and `base.first`. + /// + @inlinable public mutating func formQuotientWithRemainderReportingOverflow( + dividingBy divisor: Base.Element) -> PVO { + //=--------------------------------------= + if divisor.isZero { + return PVO(partialValue: self.first, overflow: true) + } + //=--------------------------------------= + var remainder = 0 as Base.Element + + for index in self.storage.indices.reversed() { + (self.storage[index], remainder) = divisor.dividingFullWidth(HL(high: remainder, low: self.storage[index])) + } + + return PVO(partialValue: remainder, overflow: false) + } +} diff --git a/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Multiplication.swift b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Multiplication.swift new file mode 100644 index 00000000..1daa352d --- /dev/null +++ b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Multiplication.swift @@ -0,0 +1,61 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick 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: * NBK x Strict Unsigned Integer x Multiplication +//*============================================================================* +//=----------------------------------------------------------------------------= +// MARK: + Digit + Digit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Forms the low product of multiplying `base` and `multiplicand` then adding `addend`. + /// + /// - Returns: An overflow indicator. + /// + @inlinable public mutating func multiplyReportingOverflow( + by multiplicand: Base.Element, add addend: Base.Element) -> Bool { + !self.multiplyFullWidth(by: multiplicand, add: addend).isZero + } + + /// Forms the low product of multiplying `base` and `multiplicand` then adding `addend`. + /// + /// - Returns: The high product. + /// + @inlinable public mutating func multiplyFullWidth( + by multiplicand: Base.Element, add addend: Base.Element) -> Base.Element { + Self.multiplyFullWidthCodeBlock(&self.storage, by: multiplicand, add: addend) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Algorithms (pointerless performance) + //=------------------------------------------------------------------------= + + /// Forms the low product of multiplying `base` and `multiplicand` then adding `addend`. + /// + /// - Returns: The high product. + /// + @inlinable public static func multiplyFullWidthCodeBlock( + _ base: inout Base, by multiplicand: Base.Element, add addend: Base.Element) -> Base.Element { + var carry: Base.Element = addend + + for index in base.indices { + var subproduct = base[index].multipliedFullWidth(by: multiplicand) + subproduct.high &+= Base.Element(bit: subproduct.low.addReportingOverflow(carry)) + (carry, base[index]) = subproduct as HL + } + + return carry as Base.Element + } +} diff --git a/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Shifts.swift b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Shifts.swift new file mode 100644 index 00000000..6dfb667a --- /dev/null +++ b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Shifts.swift @@ -0,0 +1,157 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick 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: * NBK x Strict Unsigned Integer x Shifts +//*============================================================================* +//=----------------------------------------------------------------------------= +// MARK: + Left +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Performs a left shift. + /// + /// - Parameters: + /// - distance: `0 <= distance < base.bitWidth` + /// + @inlinable public mutating func bitshiftLeft(by distance: Int) { + precondition(distance >= 0, NBK.callsiteOutOfBoundsInfo()) + let major = NBK .quotientDividingByBitWidthAssumingIsAtLeastZero(distance) + let minor = NBK.remainderDividingByBitWidthAssumingIsAtLeastZero(distance) + return self.bitshiftLeft(major: major, minor: minor) + } + + /// Performs a left shift. + /// + /// - Parameters: + /// - major: `0 <= major < base.endIndex` + /// - minor: `0 <= minor < Base.Element.bitWidth` + /// + @inlinable public mutating func bitshiftLeft(major: Int, minor: Int) { + //=--------------------------------------= + if minor.isZero { + return self.bitshiftLeft(major: major) + } + //=--------------------------------------= + self.bitshiftLeft(major: major, minorAtLeastOne: minor) + } + + /// Performs a left shift. + /// + /// - Parameters: + /// - major: `0 <= major < base.endIndex` + /// + @inlinable public mutating func bitshiftLeft(major: Int) { + //=--------------------------------------= + if major.isZero { return } + //=--------------------------------------= + self.bitshiftLeft(majorAtLeastOne: major) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Algorithms + //=------------------------------------------------------------------------= + + /// Performs a left shift. + /// + /// - Parameters: + /// - major: `0 <= major < base.endIndex` + /// - minor: `1 <= minor < Base.Element.bitWidth` + /// + @inlinable public mutating func bitshiftLeft(major: Int, minorAtLeastOne minor: Int) { + self.bitPattern.bitshiftLeft(environment: 0 as Base.Element, major: major, minorAtLeastOne: minor) + } + + /// Performs a left shift. + /// + /// - Parameters: + /// - major: `1 <= major < base.endIndex` + /// + @inlinable public mutating func bitshiftLeft(majorAtLeastOne major: Int) { + self.bitPattern.bitshiftLeft(environment: 0 as Base.Element, majorAtLeastOne: major) + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Right +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Performs an unsigned right shift. + /// + /// - Parameters: + /// - distance: `0 <= distance < base.bitWidth` + /// + @inlinable public mutating func bitshiftRight(by distance: Int) { + precondition(distance >= 0, NBK.callsiteOutOfBoundsInfo()) + let major = NBK .quotientDividingByBitWidthAssumingIsAtLeastZero(distance) + let minor = NBK.remainderDividingByBitWidthAssumingIsAtLeastZero(distance) + return self.bitshiftRight(major: major, minor: minor) + } + + /// Performs an unsigned right shift. + /// + /// - Parameters: + /// - major: `0 <= major < base.endIndex` + /// - bits: `0 <= minor < Base.Element.bitWidth` + /// + @inlinable public mutating func bitshiftRight(major: Int, minor: Int) { + //=--------------------------------------= + if minor.isZero { + return self.bitshiftRight(major: major) + } + //=--------------------------------------= + self.bitshiftRight(major: major, minorAtLeastOne: minor) + } + + /// Performs an unsigned right shift. + /// + /// - Parameters: + /// - major: `0 <= major < base.endIndex` + /// + @inlinable public mutating func bitshiftRight(major: Int) { + //=--------------------------------------= + if major.isZero { return } + //=--------------------------------------= + self.bitshiftRight(majorAtLeastOne: major) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Algorithms + //=------------------------------------------------------------------------= + + /// Performs an unsigned right shift. + /// + /// - Parameters: + /// - major: `0 <= major < base.endIndex` + /// - minor: `1 <= minor < Base.Element.bitWidth` + /// + @inlinable public mutating func bitshiftRight(major: Int, minorAtLeastOne minor: Int) { + self.bitPattern.bitshiftRight(environment: 0 as Base.Element, major: major, minorAtLeastOne: minor) + } + + /// Performs an unsigned right shift. + /// + /// - Parameters: + /// - major: `1 <= major < base.endIndex` + /// + @inlinable public mutating func bitshiftRight(majorAtLeastOne major: Int) { + self.bitPattern.bitshiftRight(environment: 0 as Base.Element, majorAtLeastOne: major) + } +} diff --git a/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Subtraction.swift b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Subtraction.swift new file mode 100644 index 00000000..033f3fea --- /dev/null +++ b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger+Subtraction.swift @@ -0,0 +1,308 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick 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. +//=----------------------------------------------------------------------------= +// TODO: See whether consuming arguments removes the need for inout in Swift 5.9 +//=----------------------------------------------------------------------------= + +//*============================================================================* +// MARK: * NBK x Strict Unsigned Integer x Subtraction +//*============================================================================* +// MARK: + Bit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Decrements `base` by `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrement( + by bit: Bool, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.decrement(by: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformationsx x Inout + //=------------------------------------------------------------------------= + + /// Decrements `base` by `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrement( + by bit: inout Bool, at index: inout Base.Index) { + while bit && index < self.storage.endIndex { + bit = self.storage[index].subtractReportingOverflow(1 as Base.Element.Digit) + self.storage.formIndex(after: &index) + } + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Digit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Decrements `base` by `digit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrement( + by digit: Base.Element, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool + //=--------------------------------------= + bit = self.decrement(by: digit, at: &index) + //=--------------------------------------= + return NBK.IO(index as Base.Index, overflow: bit as Bool) + } + + /// Partially decrements `base` by `digit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrementInIntersection( + by digit: Base.Element, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool + //=--------------------------------------= + bit = self.decrementInIntersection(by: digit, at: &index) + //=--------------------------------------= + return NBK.IO(index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Inout + //=------------------------------------------------------------------------= + + /// Decrements `base` by `digit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrement( + by digit: Base.Element, at index: inout Base.Index) -> Bool { + var bit = self.decrementInIntersection(by: digit, at: &index) + self.decrement(by: &bit, at: &index); return bit as Bool + } + + /// Partially decrements `base` by `digit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrementInIntersection( + by digit: Base.Element, at index: inout Base.Index) -> Bool { + defer{ self.storage.formIndex(after: &index) } + return self.storage[index].subtractReportingOverflow(digit) + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Digit + Bit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Decrements `base` by the sum of `digit` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrement( + by digit: Base.Element, plus bit: Bool, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.decrement(by: digit, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + /// Partially decrements `base` by the sum of `digit` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrementInIntersection( + by digit: Base.Element, plus bit: Bool, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit, digit: Base.Element = digit + //=--------------------------------------= + self.decrementInIntersection(by: digit, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformationsx x Inout + //=------------------------------------------------------------------------= + + /// Partially decrements `base` by the sum of `digit` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrement( + by digit: Base.Element, plus bit: inout Bool, at index: inout Base.Index) { + self.decrementInIntersection(by: digit, plus: &bit, at: &index) + self.decrement(by: &bit, at: &index) + } + + /// Partially decrements `base` by the sum of `digit` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrementInIntersection( + by digit: Base.Element, plus bit: inout Bool, at index: inout Base.Index) { + var digit: Base.Element = digit + //=--------------------------------------= + if bit { + bit = digit.addReportingOverflow(1 as Base.Element.Digit) + } + + if !bit { + bit = self.storage[index].subtractReportingOverflow(digit) + } + //=--------------------------------------= + self.storage.formIndex(after: &index) + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Elements + Bit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Decrements `base` by the sum of `elements` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrement( + by elements: some Collection, plus bit: Bool = false, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.decrement(by: elements, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + /// Partially decrements `base` by the sum of `elements` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrementInIntersection( + by elements: some Collection, plus bit: Bool = false, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.decrementInIntersection(by: elements, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Inout + //=------------------------------------------------------------------------= + + /// Decrements `base` by the sum of `elements` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrement( + by elements: some Collection, plus bit: inout Bool, at index: inout Base.Index) { + self.decrementInIntersection(by: elements, plus: &bit, at: &index) + self.decrement(by: &bit, at: &index) + } + + /// Partially decrements `base` by the sum of `elements` and `bit` at `index`. + /// + /// - This operation does not continue beyond the operand intersection. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrementInIntersection( + by elements: some Collection, plus bit: inout Bool, at index: inout Base.Index) { + for elementIndex in elements.indices { + self.decrementInIntersection(by: elements[elementIndex], plus: &bit, at: &index) + } + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Elements × Digit + Digit + Bit +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Transformations + //=------------------------------------------------------------------------= + + /// Decrements `base` by the product of `elements` and `multiplicand` at `index`, + /// and the sum of `subtrahend` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @discardableResult @inlinable public mutating func decrement( + by elements: some Collection, times multiplicand: Base.Element, + plus subtrahend: Base.Element = 0, plus bit: Bool = false, at index: Base.Index) -> NBK.IO { + var index: Base.Index = index, bit: Bool = bit + //=--------------------------------------= + self.decrement(by: elements, times: multiplicand, plus: subtrahend, plus: &bit, at: &index) + //=--------------------------------------= + return NBK.IO(index: index as Base.Index, overflow: bit as Bool) + } + + //=------------------------------------------------------------------------= + // MARK: Transformations x Inout + //=------------------------------------------------------------------------= + + /// Decrements `base` by the product of `elements` and `multiplicand` at `index`, + /// and the sum of `subtrahend` and `bit` at `index`. + /// + /// - Returns: An overflow indicator and its index in `base`. + /// + @inlinable public mutating func decrement( + by elements: some Collection, times multiplicand: Base.Element, + plus subtrahend: Base.Element, plus bit: inout Bool, at index: inout Base.Index) { + var last: Base.Element = subtrahend + + for elementsIndex in elements.indices { + var subproduct = elements[elementsIndex].multipliedFullWidth(by: multiplicand) + last = Base.Element(bit: subproduct.low.addReportingOverflow(last)) &+ subproduct.high + self.decrementInIntersection(by: subproduct.low, plus: &bit, at: &index) + } + + self.decrement(by: last, plus: &bit, at: &index) + } +} diff --git a/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger.swift b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger.swift new file mode 100644 index 00000000..fbdab3dd --- /dev/null +++ b/Sources/NBKCoreKit/Private/NBKStrictUnsignedInteger.swift @@ -0,0 +1,124 @@ +//=----------------------------------------------------------------------------= +// This source file is part of the Numberick 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: * NBK x Strict Unsigned Integer +//*============================================================================* + +extension NBK { public typealias StrictUnsignedInteger = _NBKStrictUnsignedInteger } + +//*============================================================================* +// MARK: * NBK x Strict Unsigned Integer +//*============================================================================* + +/// A nonempty collection view thing-y. +/// +/// Use pointers to prevent excessive copying. +/// +/// ### Development +/// +/// The base needs `zero` to `count` indices for performance reasons. +/// +@frozen public struct _NBKStrictUnsignedInteger where Base: NBKOffsetAccessCollection, +Base.Element: NBKCoreInteger & NBKUnsignedInteger { + + //=------------------------------------------------------------------------= + // MARK: State + //=------------------------------------------------------------------------= + + @usableFromInline var storage: Base + + //=------------------------------------------------------------------------= + // MARK: Initializers + //=------------------------------------------------------------------------= + + @inlinable public init(_ base: Base) { + self.storage = base + precondition(!base.isEmpty) + } + + @inlinable public init(unchecked base: Base) { + self.storage = base + Swift.assert(!base.isEmpty) + } + + //=------------------------------------------------------------------------= + // MARK: Accessors + //=------------------------------------------------------------------------= + + @inlinable public var base: Base { + self.storage + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Collection +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger { + + //=------------------------------------------------------------------------= + // MARK: Accessors + //=------------------------------------------------------------------------= + + @inlinable public var first: Base.Element { + self.storage[self.storage.startIndex] + } + + @inlinable public var last: Base.Element { + self.storage[self.lastIndex] + } + + @inlinable public var lastIndex: Base.Index { + self.storage.index(before: self.storage.endIndex) + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Collection x Mutable +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger where Base: MutableCollection { + + //=------------------------------------------------------------------------= + // MARK: Accessors + //=------------------------------------------------------------------------= + + @inlinable public var first: Base.Element { + get { self.storage[self.storage.startIndex] } + set { self.storage[self.storage.startIndex] = newValue } + } + + @inlinable public var last: Base.Element { + get { self.storage[self.lastIndex] } + set { self.storage[self.lastIndex] = newValue } + } +} + +//=----------------------------------------------------------------------------= +// MARK: + Views x Bit Pattern +//=----------------------------------------------------------------------------= + +extension NBK.StrictUnsignedInteger { + + //=------------------------------------------------------------------------= + // MARK: Utilities x Views + //=------------------------------------------------------------------------= + + @inlinable public var bitPattern: NBK.StrictBitPattern { + _read { + yield NBK.StrictBitPattern(unchecked: self.storage) + } + + _modify { + var view = self.bitPattern; yield &view; self = Self(unchecked: view.base) + } + } +} diff --git a/Sources/NBKDoubleWidthKit/NBKDoubleWidth+Multiplication+Digit.swift b/Sources/NBKDoubleWidthKit/NBKDoubleWidth+Multiplication+Digit.swift index c27a53f9..73d8f8c8 100644 --- a/Sources/NBKDoubleWidthKit/NBKDoubleWidth+Multiplication+Digit.swift +++ b/Sources/NBKDoubleWidthKit/NBKDoubleWidth+Multiplication+Digit.swift @@ -128,7 +128,7 @@ extension NBKDoubleWidth where High == High.Magnitude { /// - Note: The `high` and `low` parts contain the entire `overflow` from `low` to `high`. /// @_disfavoredOverload @inlinable public mutating func multiplyFullWidth(by other: Digit, add carry: Digit) -> Digit { - NBK.multiplyFullWidthLenientUnsignedInteger(&self, by: other, add: carry) + NBK.StrictUnsignedInteger.multiplyFullWidthCodeBlock(&self, by: other, add: carry) } /// Returns the `low` and `high` parts of multiplication and addition. diff --git a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Addition.swift b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Addition.swift similarity index 72% rename from Tests/NBKCoreKitTests/Private/NBK+Limbs+Addition.swift rename to Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Addition.swift index d281d929..c8b94f8e 100644 --- a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Addition.swift +++ b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Addition.swift @@ -17,16 +17,16 @@ private typealias X = [UInt64] private typealias Y = [UInt32] //*============================================================================* -// MARK: * NBK x Limbs x Addition x Unsigned +// MARK: * NBK x Strict Unsigned Integer x Addition //*============================================================================* -final class NBKTestsOnLimbsByAdditionAsUnsigned: XCTestCase { +final class NBKStrictUnsignedIntegerTestsOnAddition: XCTestCase { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - func testAddingAtIndex() { + func testAddingLargeToLargeAtIndex() { NBKAssertAdditionAtIndex([~0, ~1, ~2, ~3] as W, [3, 0, 0, 0] as W, Int(0), [ 2, ~0, ~2, ~3] as W) NBKAssertAdditionAtIndex([~0, ~1, ~2, ~3] as W, [0, 3, 0, 0] as W, Int(0), [~0, 1, ~1, ~3] as W) NBKAssertAdditionAtIndex([~0, ~1, ~2, ~3] as W, [0, 0, 3, 0] as W, Int(0), [~0, ~1, 0, ~2] as W) @@ -38,7 +38,7 @@ final class NBKTestsOnLimbsByAdditionAsUnsigned: XCTestCase { NBKAssertAdditionAtIndex([~0, ~1, ~2, ~3] as W, [3 ] as W, Int(3), [~0, ~1, ~2, ~0] as W) } - func testAddingAtIndexReportingOverflow() { + func testAddingLargeToLargeAtIndexReportingOverflow() { NBKAssertAdditionAtIndex([~0, ~0, ~0, ~0] as W, [0, 0, 0, 0] as W, Int(0), [~0, ~0, ~0, ~0] as W) NBKAssertAdditionAtIndex([~0, ~0, ~0, ~0] as W, [1, 0, 0, 0] as W, Int(0), [ 0, 0, 0, 0] as W, true) @@ -54,10 +54,10 @@ final class NBKTestsOnLimbsByAdditionAsUnsigned: XCTestCase { } //=------------------------------------------------------------------------= - // MARK: Tests x Digit + // MARK: Tests x Small (and Large) //=------------------------------------------------------------------------= - func testAddingDigitAtIndex() { + func testAddingSmallToLargeAtIndex() { NBKAssertAdditionByDigitAtIndex([ 0, 0, 0, 0] as W, UInt(3), Int(0), [ 3, 0, 0, 0] as W) NBKAssertAdditionByDigitAtIndex([~0, 0, 0, 0] as W, UInt(3), Int(0), [ 2, 1, 0, 0] as W) NBKAssertAdditionByDigitAtIndex([~0, ~0, 0, 0] as W, UInt(3), Int(0), [ 2, 0, 1, 0] as W) @@ -69,7 +69,7 @@ final class NBKTestsOnLimbsByAdditionAsUnsigned: XCTestCase { NBKAssertAdditionByDigitAtIndex([~0, ~0, ~0, 0] as W, UInt(3), Int(1), [~0, 2, 0, 1] as W) } - func testAddingDigitAtIndexReportingOverflow() { + func testAddingSmallToLargeAtIndexReportingOverflow() { NBKAssertAdditionByDigitAtIndex([~0, ~0, ~0, ~0] as W, UInt(0), Int(0), [~0, ~0, ~0, ~0] as W) NBKAssertAdditionByDigitAtIndex([~0, ~0, ~0, ~0] as W, UInt(1), Int(0), [ 0, 0, 0, 0] as W, true) @@ -81,36 +81,38 @@ final class NBKTestsOnLimbsByAdditionAsUnsigned: XCTestCase { } //*============================================================================* -// MARK: * NBK x Limbs x Addition x Assertions +// MARK: * NBK x Strict Unsigned Integer x Addition x Assertions //*============================================================================* private func NBKAssertAdditionAtIndex( _ lhs: [UInt], _ rhs: [UInt], _ index: Int, _ result: [UInt], _ overflow: Bool = false, file: StaticString = #file, line: UInt = #line) { //=------------------------------------------= - // increment: limbs + bit + let lhs = NBK.StrictUnsignedInteger(lhs) + //=------------------------------------------= + // increment: elements + bit //=------------------------------------------= brr: do { var lhs = lhs - let max = NBK.incrementSufficientUnsignedInteger(&lhs, by: rhs, plus: false, at: index) - XCTAssertEqual(lhs, result, file: file, line: line) + let max = lhs.increment(by: rhs, plus: false, at: index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs - let min = NBK.incrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let max = NBK.incrementSufficientUnsignedInteger(&lhs, by: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.incrementInIntersection(by: rhs, plus: false, at: index) + let max = lhs.increment(by: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs, rhs = rhs - let min = NBK.incrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let sfx = Array(repeating: 0 as UInt, count: lhs.suffix(from: min.index).count) - let max = NBK.incrementSufficientUnsignedIntegerInIntersection(&lhs, by: sfx, plus: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.increment(by: rhs, plus: false, at: index) + let sfx = Array(repeating: UInt(), count: lhs.base.suffix(from: min.index).count) + let max = lhs.incrementInIntersection(by: sfx, plus: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } } @@ -118,21 +120,25 @@ file: StaticString = #file, line: UInt = #line) { private func NBKAssertAdditionByDigitAtIndex( _ lhs: [UInt], _ rhs: UInt, _ index: Int, _ result: [UInt], _ overflow: Bool = false, file: StaticString = #file, line: UInt = #line) { + //=------------------------------------------= + NBKAssertAdditionAtIndex(lhs, [rhs], index, result, overflow, file: file, line: line) + //=------------------------------------------= + let lhs = NBK.StrictUnsignedInteger(lhs) //=------------------------------------------= // increment: digit //=------------------------------------------= brr: do { var lhs = lhs - let max = NBK.incrementSufficientUnsignedInteger(&lhs, by: rhs, at: index) - XCTAssertEqual(lhs, result, file: file, line: line) + let max = lhs.increment(by: rhs, at: index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs - let min = NBK.incrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, at: index) - let max = NBK.incrementSufficientUnsignedInteger(&lhs, by: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.incrementInIntersection(by: rhs, at: index) + let max = lhs.increment(by: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } //=------------------------------------------= @@ -140,25 +146,25 @@ file: StaticString = #file, line: UInt = #line) { //=------------------------------------------= brr: do { var lhs = lhs - let max = NBK.incrementSufficientUnsignedInteger(&lhs, by: rhs, plus: false, at: index) - XCTAssertEqual(lhs, result, file: file, line: line) + let max = lhs.increment(by: rhs, plus: false, at: index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs - let min = NBK.incrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let max = NBK.incrementSufficientUnsignedInteger(&lhs, by: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.incrementInIntersection(by: rhs, plus: false, at: index) + let max = lhs.increment(by: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs, rhs = rhs - let min = NBK.incrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let sfx = Array(repeating: 0 as UInt, count: lhs.suffix(from: min.index).count) - let max = NBK.incrementSufficientUnsignedIntegerInIntersection(&lhs, by: sfx, plus: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.incrementInIntersection(by: rhs, plus: false, at: index) + let sfx = Array(repeating: UInt(), count: lhs.base.suffix(from: min.index).count) + let max = lhs.incrementInIntersection(by: sfx, plus: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } } diff --git a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Division.swift b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Division.swift similarity index 65% rename from Tests/NBKCoreKitTests/Private/NBK+Limbs+Division.swift rename to Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Division.swift index b6d712ef..5ecdac99 100644 --- a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Division.swift +++ b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Division.swift @@ -17,29 +17,29 @@ private typealias X = [UInt64] private typealias Y = [UInt32] //*============================================================================* -// MARK: * NBK x Limbs x Division x Digit x Unsigned +// MARK: * NBK x Strict Unsigned Integer x Division //*============================================================================* -final class NBKTestsOnLimbsByDivisionByDigitAsUnsigned: XCTestCase { +final class NBKStrictUnsignedIntegerTestsOnDivision: XCTestCase { //=------------------------------------------------------------------------= - // MARK: Tests + // MARK: Tests x Small //=------------------------------------------------------------------------= - func testDividingByDigit() { - NBKAssertDivisionByDigit([ ] as W, UInt(1), [ ] as W, UInt( )) - NBKAssertDivisionByDigit([ ] as W, UInt(2), [ ] as W, UInt( )) + func testDividingSmallBySmall() { + NBKAssertDivisionByDigit([0] as W, UInt(1), [0] as W, UInt( )) + NBKAssertDivisionByDigit([0] as W, UInt(2), [0] as W, UInt( )) NBKAssertDivisionByDigit([7] as W, UInt(1), [7] as W, UInt( )) NBKAssertDivisionByDigit([7] as W, UInt(2), [3] as W, UInt(1)) } - func testDividingByDigitReportingOverflow() { - NBKAssertDivisionByDigit([ ] as W, UInt( ), [ ] as W, UInt( ), true) + func testDividingSmallBySmallReportingOverflow() { + NBKAssertDivisionByDigit([0] as W, UInt( ), [0] as W, UInt( ), true) NBKAssertDivisionByDigit([1] as W, UInt( ), [1] as W, UInt(1), true) NBKAssertDivisionByDigit([2] as W, UInt( ), [2] as W, UInt(2), true) } - func testDividingByDigitWithLargeDividend() { + func testDividingLargeBySmallWithLargeDividend() { NBKAssertDivisionByDigit([~2, ~4, ~6, 9] as W, UInt(2), [~1, ~2, ~3, 4] as W, UInt(1)) NBKAssertDivisionByDigit([~3, ~6, ~9, 14] as W, UInt(3), [~1, ~2, ~3, 4] as W, UInt(2)) NBKAssertDivisionByDigit([~4, ~8, ~12, 19] as W, UInt(4), [~1, ~2, ~3, 4] as W, UInt(3)) @@ -48,23 +48,25 @@ final class NBKTestsOnLimbsByDivisionByDigitAsUnsigned: XCTestCase { } //*============================================================================* -// MARK: * NBK x Limbs x Division x Assertions +// MARK: * NBK x Strict Unsigned Integer x Division x Assertions //*============================================================================* -private func NBKAssertDivisionByDigit( -_ lhs: [T], _ rhs: T, _ quotient: [T], _ remainder: T, _ overflow: Bool = false, +private func NBKAssertDivisionByDigit( +_ lhs: [UInt], _ rhs: UInt, _ quotient: [UInt], _ remainder: UInt, _ overflow: Bool = false, file: StaticString = #file, line: UInt = #line) { + //=------------------------------------------= + let lhs = NBK.StrictUnsignedInteger(lhs) //=------------------------------------------= brrrrrrrrrrr: do { var lhs = lhs - let pvo = NBK.formQuotientWithRemainderReportingOverflowAsLenientUnsignedInteger(of: &lhs, dividingBy: rhs) - XCTAssertEqual(lhs, quotient, file: file, line: line) + let pvo = lhs.formQuotientWithRemainderReportingOverflow(dividingBy: rhs) + XCTAssertEqual(lhs.base, quotient, file: file, line: line) XCTAssertEqual(pvo.partialValue, remainder, file: file, line: line) XCTAssertEqual(pvo.overflow, overflow, file: file, line: line) } //=------------------------------------------= - XCTAssertEqual(NBK.remainderReportingOverflowAsLenientUnsignedInteger(of: lhs, dividingBy: rhs).partialValue, remainder, file: file, line: line) - XCTAssertEqual(NBK.remainderReportingOverflowAsLenientUnsignedInteger(of: lhs, dividingBy: rhs).overflow, overflow, file: file, line: line) + XCTAssertEqual(lhs.remainderReportingOverflow(dividingBy: rhs).partialValue, remainder, file: file, line: line) + XCTAssertEqual(lhs.remainderReportingOverflow(dividingBy: rhs).overflow, overflow, file: file, line: line) } #endif diff --git a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Multiplication.swift b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Multiplication.swift similarity index 60% rename from Tests/NBKCoreKitTests/Private/NBK+Limbs+Multiplication.swift rename to Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Multiplication.swift index e1cf36b0..241d9d47 100644 --- a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Multiplication.swift +++ b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Multiplication.swift @@ -17,34 +17,48 @@ private typealias X = [UInt64] private typealias Y = [UInt32] //*============================================================================* -// MARK: * NBK x Limbs x Multiplication x Unsigned +// MARK: * NBK x Strict Unsigned Integer x Multiplication //*============================================================================* -final class NBKTestsOnLimbsByMultiplicationAsUnsigned: XCTestCase { +final class NBKStrictUnsignedIntegerTestsOnMultiplication: XCTestCase { //=------------------------------------------------------------------------= - // MARK: Tests x Digit + // MARK: Tests x Small //=------------------------------------------------------------------------= func testMultiplicationByDigitWithAddition() { NBKAssertMultiplicationByDigitWithAdditionAsUnsigned([~0, ~0, ~0, ~0] as W, 0, 0, [ 0, 0, 0, 0, 0] as W) NBKAssertMultiplicationByDigitWithAdditionAsUnsigned([~0, ~0, ~0, ~0] as W, 0, ~0, [~0, 0, 0, 0, 0] as W) - NBKAssertMultiplicationByDigitWithAdditionAsUnsigned([~0, ~0, ~0, ~0] as W, ~0, 0, [ 1, ~0, ~0, ~0, ~1] as W) - NBKAssertMultiplicationByDigitWithAdditionAsUnsigned([~0, ~0, ~0, ~0] as W, ~0, ~0, [ 0, 0, 0, 0, ~0] as W) + NBKAssertMultiplicationByDigitWithAdditionAsUnsigned([~0, ~0, ~0, ~0] as W, ~0, 0, [ 1, ~0, ~0, ~0, ~1] as W, true) + NBKAssertMultiplicationByDigitWithAdditionAsUnsigned([~0, ~0, ~0, ~0] as W, ~0, ~0, [ 0, 0, 0, 0, ~0] as W, true) } } //*============================================================================* -// MARK: * NBK x Limbs x Multiplication x Assertions +// MARK: * NBK x Strict Unsigned Integer x Multiplication x Assertions //*============================================================================* private func NBKAssertMultiplicationByDigitWithAdditionAsUnsigned( -_ limbs: [UInt], _ multiplicand: UInt, _ addend: UInt, _ product: [UInt], +_ lhs: [UInt], _ rhs: UInt, _ addend: UInt, _ product: [UInt], _ overflow: Bool = false, file: StaticString = #file, line: UInt = #line) { //=------------------------------------------= - var result = limbs; result.append(NBK.multiplyFullWidthLenientUnsignedInteger(&result, by: multiplicand, add: addend)) + let lhs = NBK.StrictUnsignedInteger(lhs) //=------------------------------------------= - XCTAssertEqual(result, product, file: file, line: line) + // multiplication: digit + digit + //=------------------------------------------= + brr: do { + var lhs = lhs + let top = lhs.multiplyFullWidth(by: rhs, add: addend) + XCTAssertEqual(top > 00 as UInt, overflow, file: file, line: line) + XCTAssertEqual(lhs.base + [top], product, file: file, line: line) + } + + brr: do { + var lhs = lhs + let top = lhs.multiplyReportingOverflow(by: rhs, add: addend) + XCTAssertEqual(top, overflow, file: file, line: line) + XCTAssertEqual(lhs.base, Array(product.dropLast()), file: file, line: line) + } } #endif diff --git a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Subtraction.swift b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Subtraction.swift similarity index 82% rename from Tests/NBKCoreKitTests/Private/NBK+Limbs+Subtraction.swift rename to Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Subtraction.swift index 0aa8e812..e436adf3 100644 --- a/Tests/NBKCoreKitTests/Private/NBK+Limbs+Subtraction.swift +++ b/Tests/NBKCoreKitTests/Private/NBKStrictUnsignedInteger+Subtraction.swift @@ -17,16 +17,16 @@ private typealias X = [UInt64] private typealias Y = [UInt32] //*============================================================================* -// MARK: * NBK x Limbs x Subtraction x Unsigned +// MARK: * NBK x Strict Unsigned Integer x Subtraction //*============================================================================* -final class NBKTestsOnLimbsBySubtractionAsUnsigned: XCTestCase { +final class NBKStrictUnsignedIntegerTestsOnSubtraction: XCTestCase { //=------------------------------------------------------------------------= // MARK: Tests //=------------------------------------------------------------------------= - func testSubtractingAtIndex() { + func testSubtractingLargeFromLargeAtIndex() { NBKAssertSubtractionAtIndex([ 0, 1, 2, 3] as W, [3, 0, 0, 0] as W, Int(0), [~2, 0, 2, 3] as W) NBKAssertSubtractionAtIndex([ 0, 1, 2, 3] as W, [0, 3, 0, 0] as W, Int(0), [ 0, ~1, 1, 3] as W) NBKAssertSubtractionAtIndex([ 0, 1, 2, 3] as W, [0, 0, 3, 0] as W, Int(0), [ 0, 1, ~0, 2] as W) @@ -38,7 +38,7 @@ final class NBKTestsOnLimbsBySubtractionAsUnsigned: XCTestCase { NBKAssertSubtractionAtIndex([ 0, 1, 2, 3] as W, [3 ] as W, Int(3), [ 0, 1, 2, 0] as W) } - func testSubtractingAtIndexReportingOverflow() { + func testSubtractingLargeFromLargeAtIndexReportingOverflow() { NBKAssertSubtractionAtIndex([ 0, 0, 0, 0] as W, [0, 0, 0, 0] as W, Int(0), [ 0, 0, 0, 0] as W) NBKAssertSubtractionAtIndex([ 0, 0, 0, 0] as W, [1, 0, 0, 0] as W, Int(0), [~0, ~0, ~0, ~0] as W, true) @@ -54,10 +54,10 @@ final class NBKTestsOnLimbsBySubtractionAsUnsigned: XCTestCase { } //=------------------------------------------------------------------------= - // MARK: Tests x Digit + // MARK: Tests x Small (and Large) //=------------------------------------------------------------------------= - func testSubtractingDigitAtIndex() { + func testSubtractingSmallFromLargeAtIndex() { NBKAssertSubtractionByDigitAtIndex([~0, ~0, ~0, ~0] as W, UInt(3), Int(0), [~3, ~0, ~0, ~0] as W) NBKAssertSubtractionByDigitAtIndex([ 0, ~0, ~0, ~0] as W, UInt(3), Int(0), [~2, ~1, ~0, ~0] as W) NBKAssertSubtractionByDigitAtIndex([ 0, 0, ~0, ~0] as W, UInt(3), Int(0), [~2, ~0, ~1, ~0] as W) @@ -69,7 +69,7 @@ final class NBKTestsOnLimbsBySubtractionAsUnsigned: XCTestCase { NBKAssertSubtractionByDigitAtIndex([ 0, 0, 0, ~0] as W, UInt(3), Int(1), [ 0, ~2, ~0, ~1] as W) } - func testSubtractingDigitAtIndexReportingOverflow() { + func testSubtractingSmallFromLargeAtIndexReportingOverflow() { NBKAssertSubtractionByDigitAtIndex([ 0, 0, 0, 0] as W, UInt(0), Int(0), [ 0, 0, 0, 0] as W) NBKAssertSubtractionByDigitAtIndex([ 0, 0, 0, 0] as W, UInt(1), Int(0), [~0, ~0, ~0, ~0] as W, true) @@ -145,36 +145,38 @@ final class NBKTestsOnLimbsBySubtractionAsUnsigned: XCTestCase { } //*============================================================================* -// MARK: * NBK x Limbs x Subtraction x Assertions +// MARK: * NBK x Strict Unsigned Integer x Subtraction x Assertions //*============================================================================* private func NBKAssertSubtractionAtIndex( _ lhs: [UInt], _ rhs: [UInt], _ index: Int, _ result: [UInt], _ overflow: Bool = false, file: StaticString = #file, line: UInt = #line) { //=------------------------------------------= - // decrement: limbs + bit + let lhs = NBK.StrictUnsignedInteger(lhs) + //=------------------------------------------= + // decrement: elements + bit //=------------------------------------------= brr: do { var lhs = lhs - let max = NBK.decrementSufficientUnsignedInteger(&lhs, by: rhs, plus: false, at: index) - XCTAssertEqual(lhs, result, file: file, line: line) + let max = lhs.decrement(by: rhs, plus: false, at: index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs - let min = NBK.decrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let max = NBK.decrementSufficientUnsignedInteger(&lhs, by: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.decrementInIntersection(by: rhs, plus: false, at: index) + let max = lhs.decrement(by: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs, rhs = rhs - let min = NBK.decrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let sfx = Array(repeating: 0 as UInt, count: lhs.suffix(from: min.index).count) - let max = NBK.decrementSufficientUnsignedIntegerInIntersection(&lhs, by: sfx, plus: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.decrementInIntersection(by: rhs, plus: false, at: index) + let sfx = Array(repeating: UInt(), count: lhs.base.suffix(from: min.index).count) + let max = lhs.decrementInIntersection(by: sfx, plus: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } } @@ -182,21 +184,25 @@ file: StaticString = #file, line: UInt = #line) { private func NBKAssertSubtractionByDigitAtIndex( _ lhs: [UInt], _ rhs: UInt, _ index: Int, _ result: [UInt], _ overflow: Bool = false, file: StaticString = #file, line: UInt = #line) { + //=------------------------------------------= + NBKAssertSubtractionAtIndex(lhs, [rhs], index, result, overflow, file: file, line: line) + //=------------------------------------------= + let lhs = NBK.StrictUnsignedInteger(lhs) //=------------------------------------------= // decrement: digit //=------------------------------------------= brr: do { var lhs = lhs - let max = NBK.decrementSufficientUnsignedInteger(&lhs, by: rhs, at: index) - XCTAssertEqual(lhs, result, file: file, line: line) + let max = lhs.decrement(by: rhs, at: index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs - let min = NBK.decrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, at: index) - let max = NBK.decrementSufficientUnsignedInteger(&lhs, by: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.decrementInIntersection(by: rhs, at: index) + let max = lhs.decrement(by: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } //=------------------------------------------= @@ -204,25 +210,25 @@ file: StaticString = #file, line: UInt = #line) { //=------------------------------------------= brr: do { var lhs = lhs - let max = NBK.decrementSufficientUnsignedInteger(&lhs, by: rhs, plus: false, at: index) - XCTAssertEqual(lhs, result, file: file, line: line) + let max = lhs.decrement(by: rhs, plus: false, at: index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs - let min = NBK.decrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let max = NBK.decrementSufficientUnsignedInteger(&lhs, by: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.decrementInIntersection(by: rhs, plus: false, at: index) + let max = lhs.decrement(by: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } brr: do { var lhs = lhs, rhs = rhs - let min = NBK.decrementSufficientUnsignedIntegerInIntersection(&lhs, by: rhs, plus: false, at: index) - let sfx = Array(repeating: 0 as UInt, count: lhs.suffix(from: min.index).count) - let max = NBK.decrementSufficientUnsignedIntegerInIntersection(&lhs, by: sfx, plus: min.overflow, at: min.index) - XCTAssertEqual(lhs, result, file: file, line: line) + let min = lhs.decrementInIntersection(by: rhs, plus: false, at: index) + let sfx = Array(repeating: UInt(), count: lhs.base.suffix(from: min.index).count) + let max = lhs.decrementInIntersection(by: sfx, plus: min.overflow, at: min.index) + XCTAssertEqual(lhs.base, result, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } } @@ -230,13 +236,15 @@ file: StaticString = #file, line: UInt = #line) { private func NBKAssertSubtractionByProductAtIndex( _ lhs: [UInt], _ limbs: [UInt], _ multiplicand: UInt, _ digit: UInt, _ bit: Bool, _ index: Int, _ product: [UInt], _ overflow: Bool = false, file: StaticString = #file, line: UInt = #line) { + //=------------------------------------------= + let lhs = NBK.StrictUnsignedInteger(lhs) //=------------------------------------------= // decrement: limbs × digit + digit + bit //=------------------------------------------= brr: do { var lhs = lhs - let max = NBK.decrementSufficientUnsignedInteger(&lhs, by: limbs, times: multiplicand, plus: digit, plus: bit, at: index) - XCTAssertEqual(lhs, product, file: file, line: line) + let max = lhs.decrement(by: limbs, times: multiplicand, plus: digit, plus: bit, at: index) + XCTAssertEqual(lhs.base, product, file: file, line: line) XCTAssertEqual(max.overflow, overflow, file: file, line: line) } }