diff --git a/Sources/CoreKit/Models/Count+Comparison.swift b/Sources/CoreKit/Models/Count+Comparison.swift index c9f539bc..019a63d6 100644 --- a/Sources/CoreKit/Models/Count+Comparison.swift +++ b/Sources/CoreKit/Models/Count+Comparison.swift @@ -42,4 +42,17 @@ extension Count { @inlinable public var isInfinite: Bool { Bool(self.base.appendix) } + + /// Indicates whether this value is a power of `2`. + /// + /// - Note: `log2(&0+1)` is the only representable infinite power of `2`. + /// + @inlinable public var isPowerOf2: Bool { + if self.base.isPositive { + return (self.base & self.base.decremented().unchecked()).isZero + + } else { + return (self == Self.infinity) + } + } } diff --git a/Tests/UltimathnumTests/BinaryInteger+Metadata.swift b/Tests/UltimathnumTests/BinaryInteger+Metadata.swift index 6b4cda14..083f29f5 100644 --- a/Tests/UltimathnumTests/BinaryInteger+Metadata.swift +++ b/Tests/UltimathnumTests/BinaryInteger+Metadata.swift @@ -37,10 +37,11 @@ import TestKit2 whereIs(type) func whereIs(_ type: T.Type) where T: BinaryInteger { + #expect(T.size.isPowerOf2) #expect(T.size >= T.Element .size) #expect(T.size == T.Magnitude.size) #expect(T.size == T.Signitude.size) - + Ɣexpect(MemoryLayout.self, equals: MemoryLayout.self) Ɣexpect(MemoryLayout.self, equals: MemoryLayout.self) diff --git a/Tests/UltimathnumTests/Count+Comparison.swift b/Tests/UltimathnumTests/Count+Comparison.swift index 231e008c..24e89e59 100644 --- a/Tests/UltimathnumTests/Count+Comparison.swift +++ b/Tests/UltimathnumTests/Count+Comparison.swift @@ -21,6 +21,15 @@ import TestKit2 // MARK: Tests //=------------------------------------------------------------------------= + @Test(arguments: fuzzers) + func comparisonIsLikeUnsignedIntegerComparison(_ randomness: consuming FuzzerInt) { + for _ in 0 ..< 256 { + let lhs = UX.entropic(using: &randomness) + let rhs = UX.entropic(using: &randomness) + Ɣexpect(Count(raw: lhs), equals: Count(raw: rhs), is: lhs.compared(to: rhs)) + } + } + @Test(arguments: fuzzers) func isZeroIsLikeBinaryIntegerIsZero(_ randomness: consuming FuzzerInt) { for _ in 0 ..< 256 { @@ -38,11 +47,24 @@ import TestKit2 } @Test(arguments: fuzzers) - func comparisonIsLikeUnsignedIntegerComparison(_ randomness: consuming FuzzerInt) { - for _ in 0 ..< 256 { - let lhs = UX.entropic(using: &randomness) - let rhs = UX.entropic(using: &randomness) - Ɣexpect(Count(raw: lhs), equals: Count(raw: rhs), is: lhs.compared(to: rhs)) + func isPowerOf2IsNormalExceptMaxIsPowerOf2(_ randomness: consuming FuzzerInt) { + #expect( Count(raw: UX.max).isPowerOf2) + #expect(!Count(raw: IX.min).isPowerOf2) + #expect(!Count(raw: IX.max).isPowerOf2) + #expect(!Count(raw: UX.min).isPowerOf2) + + for distance in 0 ..< IX(size: IX.self) - 1 { + #expect(Count(IX.lsb << distance).isPowerOf2) + } + + for _ in 0 ..< conditional(debug: 64, release: 256) { + let random = IX.entropic(as: .natural, using: &randomness) + let expectation: Bool = random.count(Bit.one) == Count(01) + #expect(Count(random).isPowerOf2 == expectation) + } + + for _ in 0 ..< conditional(debug: 64, release: 256) { + #expect(!Count(raw: IX.random(in: IX.min...IX(-2), using: &randomness)).isPowerOf2) } } }