Skip to content

Commit

Permalink
Lower Expect+Geometry.swift utilities (#110).
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Nov 7, 2024
1 parent ef2a262 commit 2473d2f
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 119 deletions.
28 changes: 0 additions & 28 deletions Sources/TestKit/Expect+Geometry.swift

This file was deleted.

8 changes: 8 additions & 0 deletions Sources/TestKit/Utilities+Integers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ extension BinaryInteger {
return Self(repeating: Bit.one).up(Count(((size)))).toggled()
}
}

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

@inlinable public var isNatural: Bool {
self.appendix.isZero
}
}

//*============================================================================*
Expand Down
225 changes: 146 additions & 79 deletions Tests/UltimathnumTests/BinaryInteger+Geometry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,82 @@ import TestKit
//=------------------------------------------------------------------------=

@Test(
"BinaryInteger/geometry: √(x)",
Tag.List.tags(.random),
"BinaryInteger/geometry: √(x) of examples",
Tag.List.tags(.documentation, .generic),
ParallelizationTrait.serialized,
arguments: Array<(I8, I8?)>([
(value: I8(-1), isqrt: I8?(nil)),
(value: I8( 0), isqrt: I8?( 0)),
(value: I8( 1), isqrt: I8?( 1)),
(value: I8( 2), isqrt: I8?( 1)),
(value: I8( 3), isqrt: I8?( 1)),
(value: I8( 4), isqrt: I8?( 2)),
(value: I8( 5), isqrt: I8?( 2)),
(value: I8( 6), isqrt: I8?( 2)),
(value: I8( 7), isqrt: I8?( 2)),
(value: I8( 8), isqrt: I8?( 2)),
(value: I8( 9), isqrt: I8?( 3)),
(value: I8(10), isqrt: I8?( 3)),
(value: I8(11), isqrt: I8?( 3)),
(value: I8(12), isqrt: I8?( 3)),
(value: I8(13), isqrt: I8?( 3)),
(value: I8(14), isqrt: I8?( 3)),
(value: I8(15), isqrt: I8?( 3)),
(value: I8(16), isqrt: I8?( 4)),
])) func integerSquareRootOfExampels(value: I8, isqrt: I8?) throws {
for type in typesAsBinaryInteger {
try whereIs(type)
}

func whereIs<T>(_ type: T.Type) throws where T: BinaryInteger {
if let value = T.exactly(value).optional() {
let expectation = isqrt.flatMap{T.exactly($0).optional()}
try #require(value.isqrt() == expectation)
try Ɣrequire(validating: value, isqrt: expectation)
}
}
}

@Test(
"BinaryInteger/geometry: √(x) of random",
Tag.List.tags(.generic, .random),
arguments: typesAsBinaryInteger, fuzzers
) func integerSquareRoot(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) throws {
try whereIs(type)
) func integerSquareRootOfNatural(
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 index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, as: Domain.natural, using: &randomness)
try Ɣexpect(value, isqrt: #require(value.isqrt()))
let value = T.entropic(through: index, using: &randomness)
try Ɣrequire(validating: value, isqrt: value.isqrt())
}
}
}

@Test(
"BinaryInteger/geometry: √(x) as natural",
Tag.List.tags(.random),
arguments: typesAsSystemsIntegerAsUnsigned, fuzzers
) func integerSquareRootAsNatural(type: any SystemsIntegerAsUnsigned.Type, randomness: consuming FuzzerInt) throws {
try whereIs(type)
//=------------------------------------------------------------------------=
// MARK: Utilities
//=------------------------------------------------------------------------=

private func Ɣrequire<T>(
validating value: T,
isqrt expectation: Optional<T>,
at location: SourceLocation = #_sourceLocation
) throws where T: BinaryInteger {

func whereIs<T>(_ type: T.Type) throws where T: SystemsIntegerAsUnsigned {
for _ in 0 ..< conditional(debug: 64, release: 512) {
let value = T.entropic(as: Domain.natural, using: &randomness)
try Ɣexpect(value, isqrt: value.isqrt())
try #require((expectation == nil) == !value.isNatural, sourceLocation: location)

if let expectation {
try #require(expectation.isNatural)
let low = try #require(expectation.squared().optional())
try #require(value >= low, sourceLocation: location)

let next = try #require(expectation.incremented().optional())
if let high = next.squared().optional() {
try #require(value < high, sourceLocation: location)
}
}
}
Expand All @@ -57,52 +107,25 @@ import TestKit
// MARK: * Binary Integer x Geometry x Edge Cases
//*============================================================================*

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

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

@Test("BinaryInteger/geometry: √(x) of small natural", .serialized, arguments: [
Some( 0 as U8, yields: 0 as U8),
Some( 1 as U8, yields: 1 as U8),
Some( 2 as U8, yields: 1 as U8),
Some( 3 as U8, yields: 1 as U8),
Some( 4 as U8, yields: 2 as U8),
Some( 5 as U8, yields: 2 as U8),
Some( 6 as U8, yields: 2 as U8),
Some( 7 as U8, yields: 2 as U8),
Some( 8 as U8, yields: 2 as U8),
Some( 9 as U8, yields: 3 as U8),
Some(10 as U8, yields: 3 as U8),
Some(11 as U8, yields: 3 as U8),
Some(12 as U8, yields: 3 as U8),
Some(13 as U8, yields: 3 as U8),
Some(14 as U8, yields: 3 as U8),
Some(15 as U8, yields: 3 as U8),
Some(16 as U8, yields: 4 as U8),
] as [Some<U8, U8>])
func integerSquareRootOfSmallNatural(expectation: Some<U8, U8>) throws {
for type in typesAsBinaryInteger {
try whereIs(type)
}

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

/// - Note: A binary algorithm may make a correct initial guess here.
@Test(
"BinaryInteger/geometry: √(x) of power-of-2 squares",
"BinaryInteger/geometry/edge-cases: √(x) of power-of-2 squares",
Tag.List.tags(.generic, .random),
arguments: typesAsBinaryInteger, fuzzers
) func integerSquareRootOfPowerOf2Squares(type: any BinaryInteger.Type, randomness: consuming FuzzerInt) throws {
try whereIs(type)
) func integerSquareRootOfPowerOf2Squares(
type: any BinaryInteger.Type, randomness: consuming FuzzerInt
) throws {

try whereIs(type)
func whereIs<T>(_ type: T.Type) throws where T: BinaryInteger {
for index in 0 ..< ((IX(size: T.self) ?? 256) / 2) {
let size = IX(size: T.self) ?? 256

for index in 0 ..< size / 2 {
let expectation = T.lsb << index
let power = expectation << index
try #require(power.isqrt() == expectation)
Expand All @@ -111,45 +134,89 @@ import TestKit
}

@Test(
"BinaryInteger/geometry: √(x) of negative is nil",
Tag.List.tags(.random),
"BinaryInteger/geometry/edge-cases: √(x) of negative is nil",
Tag.List.tags(.documentation, .generic, .random),
arguments: typesAsBinaryIntegerAsSigned, fuzzers
) func integerSquareRootOfNegativeIsNil(type: any SignedInteger.Type, randomness: consuming FuzzerInt) throws {
) func integerSquareRootOfNegativeIsNil(
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)

#expect(low .isqrt() == nil)
#expect(high.isqrt() == nil)

for _ in 0 ..< 32 {
let random = T.random(in: low...high, using: &randomness)
try #require(random.isNegative)
try #require(random.isqrt() == nil)
let index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, as: Domain.natural, using: &randomness).toggled()
try #require(value.isNegative)
try #require(value.isqrt() == nil)
}
}
}

@Test(
"BinaryInteger/geometry: √(x) of infinite is nil",
Tag.List.tags(.random),
"BinaryInteger/geometry/edge-cases: √(x) of infinite is nil",
Tag.List.tags(.documentation, .generic, .random),
arguments: typesAsArbitraryIntegerAsUnsigned, fuzzers
) func integerSquareRootOfInfiniteIsNil(type: any ArbitraryIntegerAsUnsigned.Type, randomness: consuming FuzzerInt) throws {
) func integerSquareRootOfInfiniteIsNil(
type: any ArbitraryIntegerAsUnsigned.Type, randomness: consuming FuzzerInt
) throws {

try whereIs(type)

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

#expect(low .isqrt() == nil)
#expect(high.isqrt() == nil)

for _ in 0 ..< 32 {
let random = T.random(in: low...high, using: &randomness)
try #require(random.isInfinite)
try #require(random.isqrt() == nil)
let index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, as: Domain.natural, using: &randomness).toggled()
try #require(value.isInfinite)
try #require(value.isqrt() == nil)
}
}
}
}

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

@Suite struct BinaryIntegerTestsOnGeometryConveniences {

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

@Test(
"BinaryInteger/geometry/conveniences: √(x) as Natural<T>",
Tag.List.tags(.forwarding, .generic, .random),
arguments: typesAsBinaryInteger, fuzzers
) func integerSquareRootAsNatural(
type: any BinaryInteger.Type, randomness: consuming FuzzerInt
) throws {

try whereIs(type)
func whereIs<T>(_ type: T.Type) throws where T: BinaryInteger {
for _ in 0 ..< 32 {
let index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, as: Domain.natural, using: &randomness)
try #require(value.isNatural)
let expectation = value.isqrt() as Optional<T>
try #require(expectation == Natural(value).isqrt() as T)
}
}
}

@Test(
"BinaryInteger/geometry/conveniences: √(x) as NaturalInteger",
Tag.List.tags(.forwarding, .generic, .random),
arguments: typesAsSystemsIntegerAsUnsigned, fuzzers
) func integerSquareRootAsNaturalInteger(
type: any SystemsIntegerAsUnsigned.Type, randomness: consuming FuzzerInt
) throws {

try whereIs(type)
func whereIs<T>(_ type: T.Type) throws where T: SystemsIntegerAsUnsigned {
for _ in 0 ..< 32 {
let value = T.entropic(using: &randomness)
try #require(value.isNatural)
let expectation = value.isqrt() as Optional<T>
try #require(expectation == value.isqrt() as T)
}
}
}
Expand Down
27 changes: 15 additions & 12 deletions Tests/UltimathnumTests/BinaryInteger+Logarithm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ import TestKit
"BinaryInteger/logarithm: ilog2() of random",
Tag.List.tags(.random, .generic),
arguments: typesAsBinaryInteger, fuzzers
) func ilog2OfRandomPositive(
) func ilog2OfRandom(
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 value = T.entropic(through: Shift.max(or: 255), using: &randomness)
let index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, using: &randomness)
try Ɣrequire(validating: value, ilog2: value.ilog2())
}
}
Expand All @@ -86,21 +87,20 @@ import TestKit
at location: SourceLocation = #_sourceLocation
) throws where T: BinaryInteger {

try #require((expectation == nil) == !value.isPositive, sourceLocation: location)

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

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

if let high = low.times(2).optional() {
#expect(value < high, sourceLocation: location)
try #require(value < high, sourceLocation: location)
}
}

} else {
try #require(!value.isPositive, sourceLocation: location)
}
}
}
Expand Down Expand Up @@ -138,7 +138,8 @@ import TestKit
try whereIs(type)
func whereIs<T>(_ type: T.Type) throws where T: SignedInteger {
for _ in 0 ..< 32 {
let value = T.entropic(through: Shift.max(or: 255), as: Domain.natural, using: &randomness).toggled()
let index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, as: Domain.natural, using: &randomness).toggled()
try #require(value.isNegative)
try #require(value.ilog2() == nil)
}
Expand All @@ -156,7 +157,8 @@ import TestKit
try whereIs(type)
func whereIs<T>(_ type: T.Type) throws where T: ArbitraryIntegerAsUnsigned {
for _ in 0 ..< 32 {
let value = T.entropic(through: Shift.max(or: 255), as: Domain.natural, using: &randomness).toggled()
let index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, as: Domain.natural, using: &randomness).toggled()
try #require(value.isInfinite)
try #require(value.ilog2() == Count(raw: -2))
}
Expand Down Expand Up @@ -185,7 +187,8 @@ import TestKit
try whereIs(type)
func whereIs<T>(_ type: T.Type) throws where T: UnsignedInteger {
for _ in 0 ..< 32 {
let value = T.entropic(through: Shift.max(or: 255), as: Domain.natural, using: &randomness)
let index = Shift<T.Magnitude>.max(or: 255)
let value = T.entropic(through: index, as: Domain.natural, using: &randomness)
if let nonzero = Nonzero(exactly: value) {
try #require(value.isPositive)
let expectation = value.ilog2() as Count?
Expand Down

0 comments on commit 2473d2f

Please sign in to comment.