Skip to content

Commit

Permalink
[NBKCoreKit] Square root by Newton's method.
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Nov 22, 2023
1 parent 9fe4585 commit 753c0d7
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 0 deletions.
73 changes: 73 additions & 0 deletions Sources/NBKCoreKit/Private/NBKProperBinaryInteger+Roots.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//=----------------------------------------------------------------------------=
// 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 Proper Binary Integer x Roots x Unsigned
//*============================================================================*

extension NBK.ProperBinaryInteger {

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

/// Returns the integer `square root` of `power` by using [this algorithm][algorithm].
///
/// [algorithm]: https://en.wikipedia.org/wiki/newton%27s_method
///
/// - Parameter power: A value in `Integer.zero ... Integer.max`.
///
@inlinable public static func squareRootByNewtonsMethod(of power: Integer) -> Integer {
precondition(!power.isLessThanZero, NBK.callsiteOutOfBoundsInfo())
return Integer(magnitude: Magnitude.squareRootByNewtonsMethod(of: power.magnitude))!
}
}

//*============================================================================*
// MARK: * NBK x Proper Binary Integer x Roots x Unsigned
//*============================================================================*

extension NBK.ProperBinaryInteger where Integer: NBKUnsignedInteger {

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

/// Returns the integer `square root` of `power` by using [this algorithm][algorithm].
///
/// [algorithm]: https://en.wikipedia.org/wiki/newton%27s_method
///
/// - Parameter power: A value in `Integer.zero ... Integer.max`.
///
/// ### Development
///
/// - TODO: Consider `update(_:)` when NBKBinaryInteger gets it.
/// - TODO: Consider `bitShift[...](by:)` when NBKBinaryInteger gets it.
///
@inlinable public static func squareRootByNewtonsMethod(of power: Integer) -> Integer {
//=--------------------------------------=
if power.isZero {
return power
}
//=--------------------------------------=
var guess: (Integer,Integer)
guess.0 = Integer(digit: 1) << ((power.bitWidth &- power.leadingZeroBitCount) &>> 1 &+ 1)
//=--------------------------------------=
repeat {

guess.1 = guess.0
guess.0 = power
guess.0 /= guess.1
guess.0 += guess.1
guess.0 >>= Int.one

} while guess.0 < guess.1
return (guess.1)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//=----------------------------------------------------------------------------=
// 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.
//=----------------------------------------------------------------------------=

#if !DEBUG

import NBKCoreKit
import XCTest

private typealias X = [UInt]
private typealias X64 = [UInt64]
private typealias X32 = [UInt32]

//*============================================================================*
// MARK: * NBK x Proper Binary Integer x Roots
//*============================================================================*

final class NBKProperBinaryIntegerBenchmarksOnRoots: XCTestCase {

typealias T = NBK.PBI

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

func testSquareRootAsInt32() {
var power = NBK.blackHoleIdentity(Int32.max)

for _ in 0 ... 5_000_000 {
NBK.blackHole(T.squareRootByNewtonsMethod(of: power))
NBK.blackHoleInoutIdentity(&power)
}
}

func testSquareRootAsInt64() {
var power = NBK.blackHoleIdentity(Int64.max)

for _ in 0 ... 5_000_000 {
NBK.blackHole(T.squareRootByNewtonsMethod(of: power))
NBK.blackHoleInoutIdentity(&power)
}
}

func testSquareRootAsUInt32() {
var power = NBK.blackHoleIdentity(UInt32.max)

for _ in 0 ... 5_000_000 {
NBK.blackHole(T.squareRootByNewtonsMethod(of: power))
NBK.blackHoleInoutIdentity(&power)
}
}

func testSquareRootAsUInt64() {
var power = NBK.blackHoleIdentity(UInt64.max)

for _ in 0 ... 5_000_000 {
NBK.blackHole(T.squareRootByNewtonsMethod(of: power))
NBK.blackHoleInoutIdentity(&power)
}
}
}

#endif
77 changes: 77 additions & 0 deletions Tests/NBKCoreKitTests/Private/NBKProperBinaryInteger+Roots.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//=----------------------------------------------------------------------------=
// 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.
//=----------------------------------------------------------------------------=

import NBKCoreKit
import XCTest

//*============================================================================*
// MARK: * NBK x Proper Binary Integer x Roots
//*============================================================================*

final class NBKProperBinaryIntegerTestsOnRoots: XCTestCase {

typealias T = NBK.PBI

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

func testSquareRoot() {
NBKAssertSquareRoot( Int8 .max, 0000000011)
NBKAssertSquareRoot( Int16.max, 0000000181)
NBKAssertSquareRoot( Int32.max, 0000046340)
NBKAssertSquareRoot( Int64.max, 3037000499)

NBKAssertSquareRoot(UInt8 .max, UInt8 .max >> 4)
NBKAssertSquareRoot(UInt16.max, UInt16.max >> 8)
NBKAssertSquareRoot(UInt32.max, UInt32.max >> 16)
NBKAssertSquareRoot(UInt64.max, UInt64.max >> 32)

for base in (0 as Int64) ... (100 as Int64) {
for power in (base * base) ..< (base + 1) * (base + 1) {
NBKAssertSquareRoot(power, base)
}
}
}
}

//*============================================================================*
// MARK: * NBK x Proper Binary Integer x Roots x Assertions
//*============================================================================*

private func NBKAssertSquareRoot<T: NBKFixedWidthInteger>(
_ power: T, _ expectation: T,
file: StaticString = #file, line: UInt = #line) {
//=------------------------------------------=
NBKAssertSquareRootAsUnsigned(power.magnitude, expectation.magnitude, file: file, line: line)
//=------------------------------------------=
let root = NBK.PBI.squareRootByNewtonsMethod(of: power)
XCTAssertGreaterThanOrEqual(power, T.zero, file: file, line: line)
XCTAssertEqual(root.magnitude, expectation.magnitude, file: file, line: line)
}

private func NBKAssertSquareRootAsUnsigned<T: NBKUnsignedInteger & NBKFixedWidthInteger>(
_ power: T, _ expectation: T,
file: StaticString = #file, line: UInt = #line) {
//=------------------------------------------=
let root = NBK.PBI.squareRootByNewtonsMethod(of: power)
let product0 = (root + 0).multipliedReportingOverflow(by: root + 0)
let product1 = (root + 1).multipliedReportingOverflow(by: root + 1)
//=------------------------------------------=
XCTAssertEqual(root, expectation, file: file, line: line)
XCTAssertFalse(product0.overflow)

if !product0.overflow {
XCTAssert(power >= product0.partialValue, file: file, line: line)
}

if !product1.overflow {
XCTAssert(power < product1.partialValue, file: file, line: line)
}
}

0 comments on commit 753c0d7

Please sign in to comment.