Skip to content

Commit

Permalink
Merge pull request #99 from oscbyspro/feature/experimental-predicate-…
Browse files Browse the repository at this point in the history
…wrapper

Feature/experimental-predicate-wrapper
  • Loading branch information
oscbyspro authored Oct 7, 2023
2 parents 5e36e11 + 06b55c3 commit a56d739
Show file tree
Hide file tree
Showing 12 changed files with 715 additions and 233 deletions.
108 changes: 59 additions & 49 deletions Sources/NBKCoreKit/Private/NBK+Division.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,87 +8,97 @@
//=----------------------------------------------------------------------------=

//*============================================================================*
// MARK: * NBK x Division x Int or UInt
// MARK: * NBK x Division
//*============================================================================*

extension NBK {

//=------------------------------------------------------------------------=
// MARK: Transformations x Int
// MARK: Transformation x where Divisor is Power of 2
//=------------------------------------------------------------------------=

/// Returns the `quotient` of dividing this value by its bit width.
/// Returns the `quotient` of dividing this the `dividend` by the `divisor`.
///
/// - Parameter value: `0 <= value <= max`
/// ### Development
///
@inlinable public static func quotientDividingByBitWidthAssumingIsAtLeastZero(_ value: Int) -> Int {
assert(value >= 0, NBK.callsiteOutOfBoundsInfo())
return Int(bitPattern: NBK.quotientDividingByBitWidth(UInt(bitPattern: value)))
}

/// Returns the `remainder` of dividing this value by its bit width.
///
/// - Parameter value: `0 <= value <= max`
///
@inlinable public static func remainderDividingByBitWidthAssumingIsAtLeastZero(_ value: Int) -> Int {
assert(value >= 0, NBK.callsiteOutOfBoundsInfo())
return Int(bitPattern: NBK.remainderDividingByBitWidth(UInt(bitPattern: value)))
}

//=------------------------------------------------------------------------=
// MARK: Transformation x UInt
//=------------------------------------------------------------------------=

/// Returns the `quotient` of dividing this value by its bit width.
/// Must use `init(bitPattern)` for performance reasons (see Int256 division).
///
/// - Parameter value: `0 <= value <= max`
///
@inlinable public static func quotientDividingByBitWidth(_ value: UInt) -> UInt {
value &>> UInt(bitPattern: UInt.bitWidth.trailingZeroBitCount)
@inlinable public static func quotient<T: NBKCoreInteger>(
of dividend: ZeroOrMore<T>, dividingBy divisor: PowerOf2<T>) -> T where T.Magnitude == UInt {
dividend.value &>> T(bitPattern: divisor.value.trailingZeroBitCount)
}

/// Returns the `remainder` of dividing this value by its bit width.
///
/// - Parameter value: `0 <= value <= max`
///
@inlinable public static func remainderDividingByBitWidth(_ value: UInt) -> UInt {
value & UInt(bitPattern: UInt.bitWidth &- 1)
/// Returns the `remainder` of dividing this the `dividend` by the `divisor`.
@inlinable public static func remainder<T: NBKCoreInteger>(
of dividend: ZeroOrMore<T>, dividingBy divisor: PowerOf2<T>) -> T {
dividend.value & (divisor.value &- 1 as T)
}
}

//*============================================================================*
// MARK: * NBK x Division x Binary Integer
// MARK: * NBK x Division x Least Positive Residue x Binary Integer By Word
//*============================================================================*

extension NBK {

//=------------------------------------------------------------------------=
// MARK: Transformations
// MARK: Transformations x Overflow
//=------------------------------------------------------------------------=

/// Returns the least positive `residue` of dividing the `dividend` by the `divisor`.
///
/// - Note: In the case of `overflow`, the result is the truncated `dividend`.
///
@inlinable public static func leastPositiveResidueReportingOverflow<T: BinaryInteger, U: NBKCoreInteger>(
of dividend: T, dividingBy divisor: U) -> PVO<U> where U.Magnitude == UInt {
NBK.bitCast(self.leastPositiveResidueReportingOverflow(of: dividend, dividingBy: divisor.magnitude))
}

/// Returns the least positive `residue` of dividing the `dividend` by the `divisor`.
///
/// - Note: In the case of `overflow`, the result is the truncated `dividend`.
///
@inlinable public static func leastPositiveResidueReportingOverflow<T>(
of dividend: T, dividingBy divisor: UInt) -> PVO<UInt> where T: BinaryInteger {
@inlinable public static func leastPositiveResidueReportingOverflow<T: BinaryInteger>(
of dividend: T, dividingBy divisor: UInt) -> PVO<UInt> {
//=--------------------------------------=
if divisor.isZero {
return PVO(dividend._lowWord, true)
}
//=--------------------------------------=
return PVO(self.leastPositiveResidue(of: dividend, dividingBy: NonZero(unchecked: divisor)), false)
}

//=------------------------------------------------------------------------=
// MARK: Transformations x where Divisor is Non Zero
//=------------------------------------------------------------------------=

/// Returns the least positive `residue` of dividing the `dividend` by the `divisor`.
@inlinable public static func leastPositiveResidue<T: BinaryInteger, U: NBKCoreInteger>(
of dividend: T, dividingBy divisor: NonZero<U>) -> U where U.Magnitude == UInt {
U(bitPattern: NBK.leastPositiveResidue(of: dividend, dividingBy: NBK.NonZero(unchecked: divisor.value.magnitude)))
}

/// Returns the least positive `residue` of dividing the `dividend` by the `divisor`.
@inlinable public static func leastPositiveResidue<T: BinaryInteger>(
of dividend: T, dividingBy divisor: NonZero<UInt>) -> UInt {
typealias SUI = StrictUnsignedInteger<T.Magnitude.Words>
//=--------------------------------------=
if divisor.isPowerOf2 {
return PVO(partialValue: dividend._lowWord & (divisor &- 1), overflow: false)
if let divisor = PowerOf2(exactly: divisor.value) {
return self.leastPositiveResidue(of: dividend, dividingBy: divisor)
}
//=--------------------------------------=
let (minus) = T.isSigned && dividend < T.zero
let (remainder, overflow) = SUI.SubSequence.remainderReportingOverflow(dividend.magnitude.words, dividingBy: divisor)
return PVO(partialValue: minus && !remainder.isZero ? (divisor &- remainder) : remainder, overflow: overflow)
let minus = T.isSigned && dividend < T.zero
let remainder = SUI.SubSequence.remainder(dividend.magnitude.words, dividingBy: divisor)
return minus && !remainder.isZero ? divisor.value &- remainder : remainder
}

/// Returns the least positive `residue` of dividing the `dividend` by `source.bitWidth`.
///
/// - Note: Numberick integers have positive, nonzero, bit widths.
///
@inlinable public static func leastPositiveResidue<T: NBKFixedWidthInteger>(
of dividend: some BinaryInteger, dividingByBitWidthOf source: T.Type) -> Int {
Int(bitPattern: NBK.leastPositiveResidueReportingOverflow(of: dividend, dividingBy: UInt(bitPattern: T.bitWidth)).partialValue)
//=------------------------------------------------------------------------=
// MARK: Transformations x where Divisor is Power of 2
//=------------------------------------------------------------------------=

/// Returns the least positive `residue` of dividing the `dividend` by the `divisor`.
@inlinable public static func leastPositiveResidue<T: BinaryInteger, U: NBKCoreInteger>(
of dividend: T, dividingBy divisor: PowerOf2<U>) -> U where U.Magnitude == UInt {
U(bitPattern: dividend._lowWord & UInt(bitPattern: divisor.value &- 1 as U))
}
}
165 changes: 165 additions & 0 deletions Sources/NBKCoreKit/Private/NBKGuarantee.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//=----------------------------------------------------------------------------=
// 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 Guarantee
//*============================================================================*

/// A property wrapper that validates some predicate on creation.
@frozen @propertyWrapper public struct _NBKGuarantee<Predicate: _NBKPredicate> {

/// The predicate of this type.
public typealias Predicate = Predicate

/// The value of this type.
public typealias Value = Predicate.Value

//=------------------------------------------------------------------------=
// MARK: State
//=------------------------------------------------------------------------=

/// A value that satisfies the predicate of this type.
public let value: Value

//=------------------------------------------------------------------------=
// MARK: Initializers
//=------------------------------------------------------------------------=

/// Wraps the given `value` or returns nil.
@inlinable public init?(exactly value: Value) {
guard Predicate.validate(value) else { return nil }
self.value = value
}

/// Wraps the given `value` or crashes in RELEASE mode.
@inlinable public init(_ value: Value, message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
precondition(Predicate.validate(value), message(), file: file, line: line)
self.value = value
}

/// Wraps the given `value` or crashes in DEBUG mode.
@inlinable public init(unchecked value: Value, message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
Swift.assert(Predicate.validate(value), message(), file: file, line: line)
self.value = value
}

/// Wraps the given `value` or crashes in RELEASE mode.
@inlinable public init(wrappedValue: Value, message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
self.init(wrappedValue, message: message(), file: file, line: line)
}

//=------------------------------------------------------------------------=
// MARK: Accessors
//=------------------------------------------------------------------------=

/// A value that satisfies the predicate of this type.
@inlinable public var wrappedValue: Value {
self.value
}

/// This predicate wrapper.
@inlinable public var projectedValue: Self {
self
}
}

//=----------------------------------------------------------------------------=
// MARK: + Aliases
//=----------------------------------------------------------------------------=

extension NBK {

/// A precondition asserting that a value is zero.
public typealias Zero<Value: NBKBinaryInteger>
= _NBKGuarantee<IsZero<Value>>

/// A precondition asserting that a value is not zero.
public typealias NonZero<Value: NBKBinaryInteger>
= _NBKGuarantee<IsNot<IsZero<Value>>>

/// A precondition asserting that a value is zero or less.
public typealias ZeroOrLess<Value: NBKBinaryInteger>
= _NBKGuarantee<IsNot<IsMoreThanZero<Value>>>

/// A precondition asserting that a value is zero or more.
public typealias ZeroOrMore<Value: NBKBinaryInteger>
= _NBKGuarantee<IsNot<IsLessThanZero<Value>>>

/// A precondition asserting that a value is less than zero.
public typealias LessThanZero<Value: NBKBinaryInteger>
= _NBKGuarantee<IsLessThanZero<Value>>

/// A precondition asserting that a value is more than zero.
public typealias MoreThanZero<Value: NBKBinaryInteger>
= _NBKGuarantee<IsMoreThanZero<Value>>

/// A precondition asserting that a value is a power of two.
public typealias PowerOf2<Value: NBKBinaryInteger>
= _NBKGuarantee<IsPowerOf2<Value>>

/// A precondition asserting that a value is not a power of two.
public typealias NonPowerOf2<Value: NBKBinaryInteger>
= _NBKGuarantee<IsNot<IsPowerOf2<Value>>>
}

//*============================================================================*
// MARK: * NBK x Guarantee x Predicate
//*============================================================================*

/// A predicate that can be referenced by the type system.
public protocol _NBKPredicate<Value> {

/// The type this predicate can validate.
associatedtype Value

/// Returns whether the given `value` satisfies this predicate.
@inlinable static func validate(_ value: Value) -> Bool
}

//=----------------------------------------------------------------------------=
// MARK: + Models
//=----------------------------------------------------------------------------=

extension NBK {

/// A predicate that returns the inverse result of another predicate.
@frozen public enum IsNot<Predicate: _NBKPredicate>: _NBKPredicate {
@inlinable static public func validate(_ value: Predicate.Value) -> Bool {
!Predicate.validate(value)
}
}

/// A predicate that returns whether a value is zero.
@frozen public enum IsZero<Value: NBKBinaryInteger>: _NBKPredicate {
@inlinable static public func validate(_ value: Value) -> Bool {
value.isZero
}
}

/// A predicate that returns whether a value is less than zero.
@frozen public enum IsLessThanZero<Value: NBKBinaryInteger>: _NBKPredicate {
@inlinable static public func validate(_ value: Value) -> Bool {
value.isLessThanZero
}
}

/// A predicate that returns whether a value is more than zero.
@frozen public enum IsMoreThanZero<Value: NBKBinaryInteger>: _NBKPredicate {
@inlinable static public func validate(_ value: Value) -> Bool {
value.isMoreThanZero
}
}

/// A predicate that returns whether a value is a power of two.
@frozen public enum IsPowerOf2<Value: NBKBinaryInteger>: _NBKPredicate {
@inlinable static public func validate(_ value: Value) -> Bool {
value.isPowerOf2
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ extension NBK.IntegerDescription {
// pointee: initialization
//=----------------------------------=
rebasing: repeat {
let chunk = NBK.SUISS.formQuotientWithRemainderReportingOverflow(
&magnitude, dividingBy: solution.power, in: ..<magnitudeEndIndex).partialValue
let chunk = NBK.SUISS.formQuotientWithRemainder(&magnitude,
dividingBy: NBK.NonZero(unchecked: solution.power),
in: Range(uncheckedBounds: (magnitude.startIndex, magnitudeEndIndex)))
magnitudeEndIndex = NBK.dropLast(from: magnitude, while:{ $0.isZero }).endIndex
chunks.baseAddress!.advanced(by: chunksIndex).initialize(to: chunk)
chunks.formIndex(after: &chunksIndex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extension NBK.IntegerDescription {

@inlinable public init?(_ solution: AnyRadixSolution<Element>) {
guard solution.power.isZero else { return nil }
Swift.assert(solution.exponent.isPowerOf2)
Swift.assert([2, 4, 16].contains(solution.base))
self.solution = solution
}
Expand All @@ -57,10 +58,6 @@ extension NBK.IntegerDescription {
self.solution.exponent
}

@inlinable public var power: Element {
self.solution.power
}

//=--------------------------------------------------------------------=
// MARK: Utilities
//=--------------------------------------------------------------------=
Expand Down
Loading

0 comments on commit a56d739

Please sign in to comment.