Skip to content

Commit

Permalink
Generic exponent (#72) and free multiplication (#71) in BinaryInteger…
Browse files Browse the repository at this point in the history
…/power(_:).
  • Loading branch information
oscbyspro committed Aug 23, 2024
1 parent 981c01b commit f73dfc4
Show file tree
Hide file tree
Showing 3 changed files with 482 additions and 79 deletions.
229 changes: 191 additions & 38 deletions Sources/CoreKit/BinaryInteger+Exponentiation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,25 @@ extension BinaryInteger {
///
/// Use `1` systems integer `exponent` type per type of `Self`.
///
@inlinable internal static func raise<T>(
_ instance: borrowing Self,
to exponent: /* borrowing */ Natural<T>
/// Magnitude.size < UX.size: UX or
/// Magnitude.size ≤ IX.max.: Magnitude
/// Magnitude.size > IX.max.: UX
///
/// - Note: The nonzero `coefficient` simplifies `error` reporting.
///
@inlinable internal static func resolve(
base: borrowing Self,
power exponent: consuming Natural<some UnsignedInteger>,
coefficient: /*borrowing*/ Nonzero<Self>
) -> Fallible<Self> {

Swift.assert(!exponent.value.isNegative)
Swift.assert(!exponent.value.isInfinite)
Swift.assert(!coefficient.value.isZero)

var power = Fallible(1 as Self)
var multiplier = Fallible(copy instance)
var exponent = (copy exponent).value
var power = Fallible((copy coefficient).value)
var multiplier = Fallible(copy base)
var exponent = exponent.value

exponentiation: while true {
if Bool(exponent.lsb) {
Expand All @@ -52,37 +60,116 @@ extension BinaryInteger {
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the `power` and `error` of `self` raised to `exponent`.
/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(1).power(2) // value: 001, error: false
/// U8(2).power(3) // value: 008, error: false
/// U8(3).power(5) // value: 243, error: false
/// U8(5).power(7) // value: 045, error: true
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
@inlinable public borrowing func power(_ exponent: /* borrowing */ Magnitude) -> Fallible<Self> {
if !Self.isArbitrary {
return Self.raise(self, to: Natural(unchecked: exponent))
/// - Note: The default `coefficient` is `1`.
///
@inlinable public /*borrowing*/ func power(
_ exponent: some UnsignedInteger,
coefficient: borrowing Nonzero<Self>
) -> Fallible<Self> {

if !Magnitude.isArbitrary {
var (magic, error) = Magnitude.exactly(exponent).components()
if (error) {
switch Bool(self.lsb) {
case true: error = self.magnitude() > 1 //........ cycle
case false: return Self.zero.veto(!self.isZero) // zeros
}
}

return Self.resolve(base: self, power: Natural(unchecked: magic), coefficient: coefficient).veto(error)
} else {
var (magic) = UX(clamping:(((exponent)))) // the allocation limit is IX.max
(((((magic)))))[Shift.min] = exponent.lsb // preserves the lsb to toggle ~0
return Self.resolve(base: self, power: Natural(unchecked: magic), coefficient: coefficient)
}
}

/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
/// - Note: The default `coefficient` is `1`.
///
@inlinable public borrowing func power(
_ exponent: some UnsignedInteger,
coefficient: borrowing Self = 1
) -> Fallible<Self> {

if let coefficient = Nonzero(exactly: copy coefficient) {
return self.power(exponent, coefficient: coefficient)
} else {
var magic = UX(clamping: (((exponent)))) // the allocation limit is IX.max
((((magic))))[Shift.min] = exponent.lsb // preserves the lsb to toggle ~0
return Self.raise(self, to: Natural(unchecked: magic))
return Fallible(copy coefficient)
}
}

/// Returns the `power` and `error` of `self` raised to `exponent`.
/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(1).power(2) // value: 001, error: false
/// U8(2).power(3) // value: 008, error: false
/// U8(3).power(5) // value: 243, error: false
/// U8(5).power(7) // value: 045, error: true
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
@inlinable public borrowing func power(_ exponent: borrowing Fallible<Magnitude>) -> Fallible<Self> {
self.power(exponent.value).veto(exponent.error)
/// - Note: The default `coefficient` is `1`.
///
@inlinable public borrowing func power(
_ exponent: borrowing Fallible<some UnsignedInteger>,
coefficient: borrowing Nonzero<Self>
) -> Fallible<Self> {
self.power(exponent.value, coefficient: coefficient).veto(exponent.error)
}

/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
/// - Note: The default `coefficient` is `1`.
///
@inlinable public borrowing func power(
_ exponent: borrowing Fallible<some UnsignedInteger>,
coefficient: borrowing Self = 1
) -> Fallible<Self> {
self.power(exponent.value, coefficient: coefficient).veto(exponent.error)
}
}

Expand All @@ -96,29 +183,95 @@ extension Fallible where Value: BinaryInteger {
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the `power` and `error` of `self` raised to `exponent`.
/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(1).power(2) // value: 001, error: false
/// U8(2).power(3) // value: 008, error: false
/// U8(3).power(5) // value: 243, error: false
/// U8(5).power(7) // value: 045, error: true
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
@inlinable public borrowing func power(_ exponent: borrowing Value.Magnitude) -> Fallible<Value> {
self.value.power(exponent).veto(self.error)
/// - Note: The default `coefficient` is `1`.
///
@inlinable public borrowing func power(
_ exponent: borrowing some UnsignedInteger,
coefficient: borrowing Nonzero<Value>
) -> Fallible<Value> {
self.value.power(exponent, coefficient: coefficient).veto(self.error)
}

/// Returns the `power` and `error` of `self` raised to `exponent`.
/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(1).power(2) // value: 001, error: false
/// U8(2).power(3) // value: 008, error: false
/// U8(3).power(5) // value: 243, error: false
/// U8(5).power(7) // value: 045, error: true
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
@inlinable public borrowing func power(_ exponent: borrowing Fallible<Value.Magnitude>) -> Fallible<Value> {
self.power(exponent.value).veto(exponent.error)
/// - Note: The default `coefficient` is `1`.
///
@inlinable public borrowing func power(
_ exponent: borrowing some UnsignedInteger,
coefficient: borrowing Value = 1
) -> Fallible<Value> {
self.value.power(exponent, coefficient: coefficient).veto(self.error)
}

/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
/// - Note: The default `coefficient` is `1`.
///
@inlinable public borrowing func power(
_ exponent: borrowing Fallible<some UnsignedInteger>,
coefficient: borrowing Nonzero<Value>
) -> Fallible<Value> {
self.power(exponent.value, coefficient: coefficient).veto(exponent.error)
}

/// Returns a `power` and an `error` indiactor.
///
/// - Returns: `pow(self, exponent) * coefficient`
///
/// ```swift
/// U8(0).power(0, coefficient: 0) // value: 0, error: false
/// U8(0).power(0, coefficient: 1) // value: 1, error: false
/// U8(0).power(1, coefficient: 2) // value: 0, error: false
/// U8(1).power(2, coefficient: 3) // value: 3, error: false
/// U8(2).power(3, coefficient: 5) // value: 40, error: false
/// U8(3).power(5, coefficient: 7) // value: 165, error: true
/// U8(5).power(7, coefficient: 11) // value: 239, error: true
/// ```
///
/// - Note: The default `coefficient` is `1`.
///
@inlinable public borrowing func power(
_ exponent: borrowing Fallible<some UnsignedInteger>,
coefficient: borrowing Value = 1
) -> Fallible<Value> {
self.power(exponent.value, coefficient: coefficient).veto(exponent.error)
}
}
55 changes: 55 additions & 0 deletions Tests/Benchmarks/Exponentiation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//=----------------------------------------------------------------------------=
// This source file is part of the Ultimathnum open source project.
//
// Copyright (c) 2023 Oscar Byström Ericsson
// Licensed under Apache License, Version 2.0
//
// See http://www.apache.org/licenses/LICENSE-2.0 for license information.
//=----------------------------------------------------------------------------=

import CoreKit
import DoubleIntKit
import InfiniIntKit
import TestKit

//*============================================================================*
// MARK: * Exponentiation
//*============================================================================*

/// - Important: Please disable code coverage because it is always on by default.
final class ExponentiationBenchmarks: XCTestCase {

//=------------------------------------------------------------------------=
// MARK: Tests
//=------------------------------------------------------------------------=

func testExponentiationAsUX() throws {
typealias T = UX

for base: UX in 0..<128 {
for exponent: UX in 0..<1000 {
blackHole(T(load: base).power(T(load: exponent)))
}
}
}

func testExponentiationAsU256() throws {
typealias T = U256

for base: UX in 0..<128 {
for exponent: UX in 0..<1000 {
blackHole(T(load: base).power(T(load: exponent)))
}
}
}

func testExponentiationAsUXL() throws {
typealias T = UXL

for base: UX in 0..<128 {
for exponent: UX in 0..<1000 {
blackHole(T(load: base).power(T(load: exponent)))
}
}
}
}
Loading

0 comments on commit f73dfc4

Please sign in to comment.