Skip to content

Commit

Permalink
Rewrite BinaryInteger+Logarithm.swift tests (#110).
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Oct 11, 2024
1 parent 822e6ca commit 12feb73
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 37 deletions.
5 changes: 3 additions & 2 deletions Sources/TestKit2/Expect+Geometry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import CoreKit
let low = try #require(expectation.squared().optional())
#expect(value >= low, sourceLocation: location)

guard let high = expectation.incremented().squared().optional() else { return }
#expect(value < high, sourceLocation: location)
if let high = expectation.incremented().squared().optional() {
#expect(value < high, sourceLocation: location)
}
}
35 changes: 35 additions & 0 deletions Sources/TestKit2/Expect+Logarithm.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//=----------------------------------------------------------------------------=
// 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: * Expect x Logarithm
//*============================================================================*

@inlinable public func Ɣexpect<T>(
_ value: T,
ilog2 expectation: Count,
at location: SourceLocation = #_sourceLocation
) throws where T: BinaryInteger {

let ilog2 = try #require(value.ilog2())
#expect(ilog2 == expectation, sourceLocation: location)
#expect(ilog2 < T.size, sourceLocation: location)
#expect(ilog2.isInfinite == value.isInfinite, sourceLocation: location)

if !ilog2.isInfinite {
let low = T.lsb.up(ilog2)
#expect(value >= low, sourceLocation: location)

if let high = low.times(2).optional() {
#expect(value < high, sourceLocation: location)
}
}
}
1 change: 1 addition & 0 deletions Sources/TestKit2/Utilities+Integers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ extension EdgyInteger {
Self.min ..< Self.lsb
}
}

//*============================================================================*
// MARK: * Utilities x Integers x Partitions
//*============================================================================*
Expand Down
6 changes: 4 additions & 2 deletions Tests/UltimathnumTests/BinaryInteger+Geometry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ import TestKit2
// MARK: Tests
//=------------------------------------------------------------------------=

@Test("BinaryInteger/isqrt() - small", .serialized, arguments: [
@Test("BinaryInteger/isqrt() - small natural", .serialized, arguments: [
Some( 0 as U8, yields: 0 as U8),
Some( 1 as U8, yields: 1 as U8),
Expand All @@ -91,7 +91,7 @@ import TestKit2
Some(16 as U8, yields: 4 as U8),
] as [Some<U8, U8>])
func integerSquareRootOfNaturalSmall(expectation: Some<U8, U8>) throws {
func integerSquareRootOfSmallNatural(expectation: Some<U8, U8>) throws {
for type in binaryIntegers {
try whereIs(type)
}
Expand Down Expand Up @@ -128,6 +128,7 @@ import TestKit2

for _ in 0 ..< 32 {
let random = T.random(in: low...high, using: &randomness)
#expect(random.isNegative)
#expect(random.isqrt() == nil)
}
}
Expand All @@ -146,6 +147,7 @@ import TestKit2

for _ in 0 ..< 32 {
let random = T.random(in: low...high, using: &randomness)
#expect(random.isInfinite)
#expect(random.isqrt() == nil)
}
}
Expand Down
141 changes: 108 additions & 33 deletions Tests/UltimathnumTests/BinaryInteger+Logarithm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,130 @@
//=----------------------------------------------------------------------------=

import CoreKit
import DoubleIntKit
import InfiniIntKit
import RandomIntKit
import TestKit
import TestKit2

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

final class BinaryIntegerTestsOnLogarithm: XCTestCase {
@Suite struct BinaryIntegerTestsOnLogarithm {

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

@Test("BinaryInteger/ilog2() - [entropic]", arguments: binaryIntegers, fuzzers)
func binaryIntegerLogarithm(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) throws {
try whereIs(type)

func whereIs<T>(_ type: T.Type) throws where T: BinaryInteger {
for _ in 0 ..< conditional(debug: 32, release: 256) {
let random = T.entropic(through: Shift.max(or: 255), using: &randomness)
if !random.isPositive {
#expect(random.ilog2() == nil)

} else {
let random = try #require(Nonzero(exactly: random))
let expectation = try #require(random.value.ilog2())

#expect(expectation == random.ilog2())
#expect(expectation == random.magnitude().ilog2())
#expect(expectation == random.magnitude().value.ilog2())

try Ɣexpect(random.value, ilog2: expectation)
}
}
}
}
}

//*============================================================================*
// MARK: * Binary Integer x Logarithm x Edge Cases
//*============================================================================*

@Suite(.tags(.documentation)) struct BinaryIntegerTestsOnLogarithmEdgeCases {

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

func testBinaryLogarithmForSmallEntropies() {
@Test("BinaryInteger/ilog2() - small positive", .serialized, arguments: [
Some( 1 as U8, yields: Count(0)),
Some( 2 as U8, yields: Count(1)),
Some( 3 as U8, yields: Count(1)),
Some( 4 as U8, yields: Count(2)),
Some( 5 as U8, yields: Count(2)),
Some( 6 as U8, yields: Count(2)),
Some( 7 as U8, yields: Count(2)),
Some( 8 as U8, yields: Count(3)),
Some( 9 as U8, yields: Count(3)),
Some(10 as U8, yields: Count(3)),
Some(11 as U8, yields: Count(3)),
Some(12 as U8, yields: Count(3)),
Some(13 as U8, yields: Count(3)),
Some(14 as U8, yields: Count(3)),
Some(15 as U8, yields: Count(3)),
Some(16 as U8, yields: Count(4)),
] as [Some<U8, Count>])
func binaryIntegerLogarithmOfSmallPositive(expectation: Some<U8, Count>) throws {
for type in binaryIntegers {
try whereIs(type)
}

func whereIs<T>(_ type: T.Type) throws where T: BinaryInteger {
try Ɣexpect(T(expectation.input), ilog2: expectation.output)
}
}

@Test("BinaryInteger/ilog2() - zero is nil [uniform]", arguments: binaryIntegersWhereIsSigned)
func binaryIntegerLogarithmOfZeroIsNil(type: any BinaryInteger.Type) {
whereIs(type)

func whereIs<T>(_ type: T.Type) where T: BinaryInteger {
let max = Count(raw: IX(raw: T.size) - 1)

func check(_ value: T, _ expectation: Count?) {
Test().same(value.ilog2(), expectation)
Test().same(Nonzero(exactly: value)?.ilog2(), expectation)
}
#expect(T.zero.ilog2() == nil)
}
}

@Test("BinaryInteger/ilog2() - negative is nil [uniform]", arguments: binaryIntegersWhereIsSigned, fuzzers)
func binaryIntegerLogarithmOfNegativeIsNil(type: any SignedInteger.Type, randomness: consuming FuzzerInt) throws {
try whereIs(type)

func whereIs<T>(_ type: T.Type) throws where T: SignedInteger {
let low = T(repeating: Bit.one).up(Shift.max(or: 255))
let high = T(repeating: Bit.one)

check( 0 as T, nil)
check( 1 as T, Count(0))
check( 2 as T, Count(1))
check( 3 as T, Count(1))
check( 4 as T, Count(2))
check( 5 as T, Count(2))
check( 6 as T, Count(2))
check( 7 as T, Count(2))
check( 8 as T, Count(3))
check( 9 as T, Count(3))
#expect(low .ilog2() == nil)
#expect(high.ilog2() == nil)

check(~9 as T, T.isSigned ? nil : max)
check(~8 as T, T.isSigned ? nil : max)
check(~7 as T, T.isSigned ? nil : max)
check(~6 as T, T.isSigned ? nil : max)
check(~5 as T, T.isSigned ? nil : max)
check(~4 as T, T.isSigned ? nil : max)
check(~3 as T, T.isSigned ? nil : max)
check(~2 as T, T.isSigned ? nil : max)
check(~1 as T, T.isSigned ? nil : max)
check(~0 as T, T.isSigned ? nil : max)
for _ in 0 ..< 32 {
let random = T.random(in: low...high, using: &randomness)
#expect(random.isNegative)
#expect(random.ilog2() == nil)
}
}
}

@Test("BinaryInteger/ilog2() - infinite is one less than size [uniform]", arguments: arbitraryIntegersWhereIsUnsigned, fuzzers)
func binaryIntegerLogarithmOfInfiniteIsOneLessThanSize(type: any ArbitraryIntegerWhereIsUnsigned.Type, randomness: consuming FuzzerInt) throws {
try whereIs(type)

for type in binaryIntegers {
whereIs(type)
func whereIs<T>(_ type: T.Type) throws where T: ArbitraryIntegerWhereIsUnsigned {
let low = T(repeating: Bit.one).up(Shift.max(or: 255))
let high = T(repeating: Bit.one)
let expectation = Count(raw: -2)

#expect(expectation == Count(raw: IX(raw: T.size) - 1))
#expect(low .ilog2() == expectation)
#expect(high.ilog2() == expectation)

for _ in 0 ..< 32 {
let random = T.random(in: low...high, using: &randomness)
#expect(random.isInfinite)
#expect(random.ilog2() == expectation)
}
}
}
}

0 comments on commit 12feb73

Please sign in to comment.