Skip to content

Commit

Permalink
Add: BinaryInteger/isqrt() (#76).
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Aug 29, 2024
1 parent 003d50c commit 31f123e
Show file tree
Hide file tree
Showing 5 changed files with 459 additions and 2 deletions.
242 changes: 242 additions & 0 deletions Sources/CoreKit/BinaryInteger+Geometry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
//=----------------------------------------------------------------------------=
// This source file is part of the Ultimathnum open source project.
//
// Copyright (c) 2023 Oscar Byström Ericsson
// Licensed under Apache License, Version 2.0
//
// See http://www.apache.org/licenses/LICENSE-2.0 for license information.
//=----------------------------------------------------------------------------=

//*============================================================================*
// MARK: * Binary Integer x Geometry
//*============================================================================*

extension BinaryInteger {

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the integer square root of `self`.
///
/// ```swift
/// I8( 4).isqrt() // 2
/// I8( 3).isqrt() // 1
/// I8( 2).isqrt() // 1
/// I8( 1).isqrt() // 1
/// I8( 0).isqrt() // 0
/// I8(-1).isqrt() // nil
/// I8(-2).isqrt() // nil
/// I8(-3).isqrt() // nil
/// I8(-4).isqrt() // nil
/// ```
///
/// - Note: `Natural<T>` returns nonoptional results.
///
/// ```swift
/// UXL(repeating: 1).isqrt() // nil
/// ```
///
/// - Note: Infinite square roots are `nil` on finite machines.
///
@inlinable public func isqrt() -> Optional<Self> {
Natural(exactly: self)?.isqrt()
}
}

//=----------------------------------------------------------------------------=
// MARK: + Guarantees
//=----------------------------------------------------------------------------=

extension Natural {

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the integer square root of `self`.
///
/// ```swift
/// I8( 4).isqrt() // 2
/// I8( 3).isqrt() // 1
/// I8( 2).isqrt() // 1
/// I8( 1).isqrt() // 1
/// I8( 0).isqrt() // 0
/// I8(-1).isqrt() // nil
/// I8(-2).isqrt() // nil
/// I8(-3).isqrt() // nil
/// I8(-4).isqrt() // nil
/// ```
///
/// - Note: `Natural<T>` returns nonoptional results.
///
/// ```swift
/// UXL(repeating: 1).isqrt() // nil
/// ```
///
/// - Note: Infinite square roots are `nil` on finite machines.
///
@inlinable public func isqrt() -> Value {
guard let instance = Nonzero(exactly: self.value) else {
return Value.zero
}

let magnitude = Value.Magnitude.isqrt(natural: Nonzero(raw: instance))
return Value.init(raw: magnitude.value)
}
}

//*============================================================================*
// MARK: * Binary Integer x Geometry x Unsigned
//*============================================================================*

extension UnsignedInteger {

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the integer square root.
///
/// - Requires: `instance ∈ ℕ `
/// - Requires: `instance ≠ 0 `
///
/// ### Algorithm
///
/// - Seealso: https://en.wikipedia.org/wiki/newton's_method
///
/// - Seealso: https://en.wikipedia.org/wiki/integer_square_root
///
@inlinable internal static func isqrt(
natural instance: Nonzero<Self>
) -> Nonzero<Self> {

Swift.assert(!instance.value.isInfinite)
Swift.assert(!instance.value.isNegative)

var guess: Nonzero<Self> // must be overestimated initially
let offset = UX(raw: instance.ilog2()).down(Shift.one) &+ 1
var revision = Self.lsb.up(Shift(unchecked: Count(raw: offset)))

repeat {

guess = Nonzero(unchecked: consume revision)
revision = instance.value.quotient(guess).unchecked()
revision = (consume revision).plus(guess.value).unchecked()
revision = (consume revision).down(Shift.one)

} while revision < guess.value
return ((((((consume guess))))))
}
}

//*============================================================================*
// MARK: * Binary Integer x Geometry x Unsigned x Systems
//*============================================================================*

extension BinaryInteger where Self: UnsignedInteger & SystemsInteger {

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the integer square root of `self`.
///
/// ```swift
/// I8( 4).isqrt() // 2
/// I8( 3).isqrt() // 1
/// I8( 2).isqrt() // 1
/// I8( 1).isqrt() // 1
/// I8( 0).isqrt() // 0
/// I8(-1).isqrt() // nil
/// I8(-2).isqrt() // nil
/// I8(-3).isqrt() // nil
/// I8(-4).isqrt() // nil
/// ```
///
/// - Note: `Natural<T>` returns nonoptional results.
///
/// ```swift
/// UXL(repeating: 1).isqrt() // nil
/// ```
///
/// - Note: Infinite square roots are `nil` on finite machines.
///
@inlinable public func isqrt() -> Self {
Nonzero(exactly: self)?.isqrt() ?? Self.zero
}
}

//=----------------------------------------------------------------------------=
// MARK: + Recoverable
//=----------------------------------------------------------------------------=

extension Fallible where Value: UnsignedInteger & SystemsInteger {

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the integer square root of `self`.
///
/// ```swift
/// I8( 4).isqrt() // 2
/// I8( 3).isqrt() // 1
/// I8( 2).isqrt() // 1
/// I8( 1).isqrt() // 1
/// I8( 0).isqrt() // 0
/// I8(-1).isqrt() // nil
/// I8(-2).isqrt() // nil
/// I8(-3).isqrt() // nil
/// I8(-4).isqrt() // nil
/// ```
///
/// - Note: `Natural<T>` returns nonoptional results.
///
/// ```swift
/// UXL(repeating: 1).isqrt() // nil
/// ```
///
/// - Note: Infinite square roots are `nil` on finite machines.
///
@inlinable public func isqrt() -> Self {
self.value.isqrt().veto(self.error)
}
}

//=----------------------------------------------------------------------------=
// MARK: + Guarantees
//=----------------------------------------------------------------------------=

extension Nonzero where Value: UnsignedInteger & SystemsInteger {

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=

/// Returns the integer square root of `self`.
///
/// ```swift
/// I8( 4).isqrt() // 2
/// I8( 3).isqrt() // 1
/// I8( 2).isqrt() // 1
/// I8( 1).isqrt() // 1
/// I8( 0).isqrt() // 0
/// I8(-1).isqrt() // nil
/// I8(-2).isqrt() // nil
/// I8(-3).isqrt() // nil
/// I8(-4).isqrt() // nil
/// ```
///
/// - Note: `Natural<T>` returns nonoptional results.
///
/// ```swift
/// UXL(repeating: 1).isqrt() // nil
/// ```
///
/// - Note: Infinite square roots are `nil` on finite machines.
///
@inlinable public func isqrt() -> Value {
Value.isqrt(natural: self).value
}
}
31 changes: 31 additions & 0 deletions Sources/TestKit/Test+Geometry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//=----------------------------------------------------------------------------=
// 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

//*============================================================================*
// MARK: * Text x Geometry
//*============================================================================*

extension Test {

//=------------------------------------------------------------------------=
// MARK: Utilities
//=------------------------------------------------------------------------=

public func isqrt<T>(_ instance: T, _ expectation: T?) where T: BinaryInteger {
always: do {
same(instance.isqrt(), expectation)
}

if let natural = UX.exactly(instance).optional() {
same(natural.isqrt(), expectation.map(UX.init(load:)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import InfiniIntKit
import TestKit

//*============================================================================*
// MARK: * Exponentiation
// MARK: * Binary Integer x Exponentiation
//*============================================================================*

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

//=------------------------------------------------------------------------=
// MARK: Tests
Expand Down
49 changes: 49 additions & 0 deletions Tests/Benchmarks/BinaryInteger+Geometry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//=----------------------------------------------------------------------------=
// 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: * Binary Integer x Geometry
//*============================================================================*

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

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

func testSquareRootsAsUX() {
typealias T = UX

for power: UX in 0 ..< 1_000_000 {
blackHole(T(load: power).isqrt())
}
}

func testSquareRootsAsU256() {
typealias T = U256

for power: UX in 0 ..< 1_000_000 {
blackHole(T(load: power).isqrt())
}
}

func testSquareRootsAsUXL() {
typealias T = UXL

for power: UX in 0 ..< 1_000_000 {
blackHole(T(load: power).isqrt())
}
}
}
Loading

0 comments on commit 31f123e

Please sign in to comment.