From 686fe53c74ea718bf330ea114cb67aff2d6d5901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Bystr=C3=B6m=20Ericsson?= Date: Fri, 30 Aug 2024 07:43:03 +0200 Subject: [PATCH] Protocol: Guarantee (#62). --- README.md | 6 +- Sources/CoreKit/Guarantee+Validation.swift | 55 ++++++++++++ Sources/CoreKit/Guarantee.swift | 51 +++++++++++ .../CoreKit/Models/Guarantees/Finite.swift | 56 +++--------- .../CoreKit/Models/Guarantees/Natural.swift | 52 ++---------- .../CoreKit/Models/Guarantees/Nonzero.swift | 52 ++---------- Sources/CoreKit/Models/Guarantees/Shift.swift | 50 ++--------- Tests/UltimathnumTests/Guarantee.swift | 65 ++++++++++++++ .../UltimathnumTests/Guarantees/Finite.swift | 32 ++++--- .../UltimathnumTests/Guarantees/Natural.swift | 34 ++++---- .../UltimathnumTests/Guarantees/Nonzero.swift | 85 +++++-------------- Tests/UltimathnumTests/Guarantees/Shift.swift | 84 +++++------------- 12 files changed, 282 insertions(+), 340 deletions(-) create mode 100644 Sources/CoreKit/Guarantee+Validation.swift create mode 100644 Sources/CoreKit/Guarantee.swift create mode 100644 Tests/UltimathnumTests/Guarantee.swift diff --git a/README.md b/README.md index 589e5b91..e1272d11 100644 --- a/README.md +++ b/README.md @@ -236,12 +236,12 @@ compose better than others. A trusted input delegates precondition checks to the so that complex types can be built with less overhead. The type system will ask you to accept or reject such inputs. Your validation strategy will either make your code safer or easier to audit. - ```swift -init(_:) // error: traps init(_:prune:) // error: throws init(exactly:) // error: nil -init(unchecked:) // error: unchecked +init(_:) // error: precondition +init(unchecked:) // error: assert +init(unsafe:) // error: %%%%%% ``` diff --git a/Sources/CoreKit/Guarantee+Validation.swift b/Sources/CoreKit/Guarantee+Validation.swift new file mode 100644 index 00000000..affa97c6 --- /dev/null +++ b/Sources/CoreKit/Guarantee+Validation.swift @@ -0,0 +1,55 @@ +//=----------------------------------------------------------------------------= +// 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: * Guarantee x Validation +//*============================================================================* + +extension Guarantee { + + //=------------------------------------------------------------------------= + // MARK: Initializers + //=------------------------------------------------------------------------= + + /// Creates a new instance by trapping on failure. + /// + /// - Requires: The given `value` must satisfy the `predicate` of this type. + /// + @inlinable public init(_ value: consuming Value) { + precondition(Self.predicate(value), String.brokenInvariant()) + self.init(unsafe:/**/value) + } + + /// Creates a new instance by trapping on failure in debug mode. + /// + /// - Requires: The given `value` must satisfy the `predicate` of this type. + /// + @inlinable public init(unchecked value: consuming Value) { + Swift.assert(Self.predicate(value), String.brokenInvariant()) + self.init(unsafe:/**/value) + } + + /// Creates a new instance by returning `nil` on failure. + /// + /// - Requires: The given `value` must satisfy the `predicate` of this type. + /// + @inlinable public init?(exactly value: consuming Value) { + guard Self.predicate(value) else { return nil } + self.init(unsafe:/**/value) + } + + /// Creates a new instance by throwing the `error()` on failure. + /// + /// - Requires: The given `value` must satisfy the `predicate` of this type. + /// + @inlinable public init(_ value: consuming Value, prune error: @autoclosure () -> Error) throws where Error: Swift.Error { + guard Self.predicate(value) else { throw error() } + self.init(unsafe:/**/value) + } +} diff --git a/Sources/CoreKit/Guarantee.swift b/Sources/CoreKit/Guarantee.swift new file mode 100644 index 00000000..5eba372f --- /dev/null +++ b/Sources/CoreKit/Guarantee.swift @@ -0,0 +1,51 @@ +//=----------------------------------------------------------------------------= +// 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: * Guarantee +//*============================================================================* + +/// A trusted input type. +/// +/// ### Trusted Input +/// +/// This is a trusted input type. Validate inputs with these methods: +/// +/// ```swift +/// init(_:prune:) // error: throws +/// init(exactly:) // error: nil +/// init(_:) // error: precondition +/// init(unchecked:) // error: assert +/// init(unsafe:) // error: %%%%%% +/// ``` +/// +public protocol Guarantee { + + associatedtype Value + + //=------------------------------------------------------------------------= + // MARK: Metadata + //=------------------------------------------------------------------------= + + /// Indicates whether the given `value` satisfies its `predicate`. + @inlinable static func predicate(_ value: borrowing Value) -> Bool + + //=------------------------------------------------------------------------= + // MARK: Initializers + //=------------------------------------------------------------------------= + + /// Creates a new instance without validation. + /// + /// - Requires: The given `value` must satisfy the `predicate` of this type. + /// + @inlinable init(unsafe value: consuming Value) + + /// Consumes `self` and returns its `value`. + @inlinable consuming func payload() -> Value +} diff --git a/Sources/CoreKit/Models/Guarantees/Finite.swift b/Sources/CoreKit/Models/Guarantees/Finite.swift index bc7ec6bb..e871392e 100644 --- a/Sources/CoreKit/Models/Guarantees/Finite.swift +++ b/Sources/CoreKit/Models/Guarantees/Finite.swift @@ -8,7 +8,7 @@ //=----------------------------------------------------------------------------= //*============================================================================* -// MARK: * Natural +// MARK: * Finite //*============================================================================* /// A finite value. @@ -18,24 +18,19 @@ /// This is a trusted input type. Validate inputs with these methods: /// /// ```swift -/// init(_:) // error: traps /// init(_:prune:) // error: throws /// init(exactly:) // error: nil -/// init(unchecked:) // error: unchecked +/// init(_:) // error: precondition +/// init(unchecked:) // error: assert +/// init(unsafe:) // error: %%%%%% /// ``` /// -@frozen public struct Finite: Equatable where Value: BinaryInteger { - - public typealias Value = Value - +@frozen public struct Finite: Equatable, Guarantee where Value: BinaryInteger { + //=------------------------------------------------------------------------= // MARK: Metadata //=------------------------------------------------------------------------= - /// Indicates whether the given `value` can be trusted. - /// - /// - Returns: `value ∈ ℤ` - /// @inlinable public static func predicate(_ value: /* borrowing */ Value) -> Bool { !value.isInfinite // await borrowing fix } @@ -50,49 +45,18 @@ // MARK: Initializers //=------------------------------------------------------------------------= - /// Creates a new instance without validation in release mode. - /// - /// - Requires: `value ∈ ℤ` - /// - /// - Warning: Only use this method when you know the `value` is valid. - /// - @_disfavoredOverload // collection.map(Self.init) - @inlinable public init(unchecked value: consuming Value) { - Swift.assert(Self.predicate(value), String.brokenInvariant()) - self.value = value - } - - /// Creates a new instance by trapping on failure. - /// - /// - Requires: `value ∈ ℤ` - /// - @inlinable public init(_ value: consuming Value) { - precondition(Self.predicate(value), String.brokenInvariant()) + @inlinable public init(unsafe value: consuming Value) { self.value = value } - /// Creates a new instance by returning `nil` on failure. - /// - /// - Requires: `value ∈ ℤ` - /// - @inlinable public init?(exactly value: consuming Value) { - guard Self.predicate(value) else { return nil } - self.value = value - } - - /// Creates a new instance by throwing the `error()` on failure. - /// - /// - Requires: `value ∈ ℤ` - /// - @inlinable public init(_ value: consuming Value, prune error: @autoclosure () -> Error) throws where Error: Swift.Error { - guard Self.predicate(value) else { throw error() } - self.value = value + @inlinable public consuming func payload() -> Value { + self.value } //=------------------------------------------------------------------------= // MARK: Transformations //=------------------------------------------------------------------------= - + /// The `magnitude` of `self`. @inlinable public consuming func magnitude() -> Finite { Finite(unchecked: self.value.magnitude()) diff --git a/Sources/CoreKit/Models/Guarantees/Natural.swift b/Sources/CoreKit/Models/Guarantees/Natural.swift index b8b854a9..175db0b7 100644 --- a/Sources/CoreKit/Models/Guarantees/Natural.swift +++ b/Sources/CoreKit/Models/Guarantees/Natural.swift @@ -18,10 +18,11 @@ /// This is a trusted input type. Validate inputs with these methods: /// /// ```swift -/// init(_:) // error: traps /// init(_:prune:) // error: throws /// init(exactly:) // error: nil -/// init(unchecked:) // error: unchecked +/// init(_:) // error: precondition +/// init(unchecked:) // error: assert +/// init(unsafe:) // error: %%%%%% /// ``` /// /// ### How to bit cast a `Natural` @@ -31,18 +32,12 @@ /// however, that the inverse case is not as simple. `U8(255)` is natural, /// for example, but it becomes negative when you reinterpret it as `I8(-1)`. /// -@frozen public struct Natural: Equatable where Value: BinaryInteger { - - public typealias Value = Value - +@frozen public struct Natural: Equatable, Guarantee where Value: BinaryInteger { + //=------------------------------------------------------------------------= // MARK: Metadata //=------------------------------------------------------------------------= - /// Indicates whether the given `value` can be trusted. - /// - /// - Returns: `value ∈ ℕ` - /// @inlinable public static func predicate(_ value: /* borrowing */ Value) -> Bool { !Bool(value.appendix) // await borrowing fix } @@ -57,43 +52,12 @@ // MARK: Initializers //=------------------------------------------------------------------------= - /// Creates a new instance without validation in release mode. - /// - /// - Requires: `value ∈ ℕ` - /// - /// - Warning: Only use this method when you know the `value` is valid. - /// - @_disfavoredOverload // collection.map(Self.init) - @inlinable public init(unchecked value: consuming Value) { - Swift.assert(Self.predicate(value), String.brokenInvariant()) - self.value = value - } - - /// Creates a new instance by trapping on failure. - /// - /// - Requires: `value ∈ ℕ` - /// - @inlinable public init(_ value: consuming Value) { - precondition(Self.predicate(value), String.brokenInvariant()) - self.value = value - } - - /// Creates a new instance by returning `nil` on failure. - /// - /// - Requires: `value ∈ ℕ` - /// - @inlinable public init?(exactly value: consuming Value) { - guard Self.predicate(value) else { return nil } + @inlinable public init(unsafe value: consuming Value) { self.value = value } - /// Creates a new instance by throwing the `error()` on failure. - /// - /// - Requires: `value ∈ ℕ` - /// - @inlinable public init(_ value: consuming Value, prune error: @autoclosure () -> Error) throws where Error: Swift.Error { - guard Self.predicate(value) else { throw error() } - self.value = value + @inlinable public consuming func payload() -> Value { + self.value } //=------------------------------------------------------------------------= diff --git a/Sources/CoreKit/Models/Guarantees/Nonzero.swift b/Sources/CoreKit/Models/Guarantees/Nonzero.swift index ad2f582a..04c3488d 100644 --- a/Sources/CoreKit/Models/Guarantees/Nonzero.swift +++ b/Sources/CoreKit/Models/Guarantees/Nonzero.swift @@ -18,26 +18,21 @@ /// This is a trusted input type. Validate inputs with these methods: /// /// ```swift -/// init(_:) // error: traps /// init(_:prune:) // error: throws /// init(exactly:) // error: nil -/// init(unchecked:) // error: unchecked +/// init(_:) // error: precondition +/// init(unchecked:) // error: assert +/// init(unsafe:) // error: %%%%%% /// ``` /// -@frozen public struct Nonzero: BitCastable, Equatable where Value: BinaryInteger { - - public typealias Value = Value - +@frozen public struct Nonzero: BitCastable, Equatable, Guarantee where Value: BinaryInteger { + public typealias BitPattern = Nonzero //=------------------------------------------------------------------------= // MARK: Metadata //=------------------------------------------------------------------------= - /// Indicates whether the given `value` can be trusted. - /// - /// - Returns: `value ≠ 0` - /// @inlinable public static func predicate(_ value: /* borrowing */ Value) -> Bool { !value.isZero // await borrowing fix } @@ -52,43 +47,12 @@ // MARK: Initializers //=------------------------------------------------------------------------= - /// Creates a new instance without validation in release mode. - /// - /// - Requires: `value ≠ 0` - /// - /// - Warning: Only use this method when you know the `value` is valid. - /// - @_disfavoredOverload // collection.map(Self.init) - @inlinable public init(unchecked value: consuming Value) { - Swift.assert(Self.predicate(value), String.brokenInvariant()) - self.value = value - } - - /// Creates a new instance by trapping on failure. - /// - /// - Requires: `value ≠ 0` - /// - @inlinable public init(_ value: consuming Value) { - precondition(Self.predicate(value), String.brokenInvariant()) - self.value = value - } - - /// Creates a new instance by returning `nil` on failure. - /// - /// - Requires: `value ≠ 0` - /// - @inlinable public init?(exactly value: consuming Value) { - guard Self.predicate(value) else { return nil } + @inlinable public init(unsafe value: consuming Value) { self.value = value } - /// Creates a new instance by throwing the `error()` on failure. - /// - /// - Requires: `value ≠ 0` - /// - @inlinable public init(_ value: consuming Value, prune error: @autoclosure () -> Error) throws where Error: Swift.Error { - guard Self.predicate(value) else { throw error() } - self.value = value + @inlinable public consuming func payload() -> Value { + self.value } //=------------------------------------------------------------------------= diff --git a/Sources/CoreKit/Models/Guarantees/Shift.swift b/Sources/CoreKit/Models/Guarantees/Shift.swift index 63645731..cec8d7c8 100644 --- a/Sources/CoreKit/Models/Guarantees/Shift.swift +++ b/Sources/CoreKit/Models/Guarantees/Shift.swift @@ -18,26 +18,23 @@ /// This is a trusted input type. Validate inputs with these methods: /// /// ```swift -/// init(_:) // error: traps /// init(_:prune:) // error: throws /// init(exactly:) // error: nil -/// init(unchecked:) // error: unchecked +/// init(_:) // error: precondition +/// init(unchecked:) // error: assert +/// init(unsafe:) // error: %%%%%% /// ``` /// -@frozen public struct Shift: Equatable where Target: UnsignedInteger { +@frozen public struct Shift: Equatable, Guarantee where Target: UnsignedInteger { public typealias Target = Target - public typealias Value = Count + public typealias Value = Count //=------------------------------------------------------------------------= // MARK: Metadata //=------------------------------------------------------------------------= - /// Indicates whether the given `value` can be trusted. - /// - /// - Returns: `value ∈ 0 up to Target.size` - /// @inlinable public static func predicate(_ value: borrowing Value) -> Bool { value < Target.size } @@ -89,43 +86,12 @@ // MARK: Initializers //=------------------------------------------------------------------------= - /// Creates a new instance without validation in release mode. - /// - /// - Requires: `value ∈ 0 up to Target.size` - /// - /// - Warning: Only use this method when you know the `value` is valid. - /// - @_disfavoredOverload // collection.map(Self.init) - @inlinable public init(unchecked value: consuming Value) { - Swift.assert(Self.predicate(value), String.brokenInvariant()) - self.value = value - } - - /// Creates a new instance by trapping on failure. - /// - /// - Requires: `value ∈ 0 up to Target.size` - /// - @inlinable public init(_ value: consuming Value) { - precondition(Self.predicate(value), String.brokenInvariant()) - self.value = value - } - - /// Creates a new instance by returning `nil` on failure. - /// - /// - Requires: `value ∈ 0 up to Target.size` - /// - @inlinable public init?(exactly value: consuming Value) { - guard Self.predicate(value) else { return nil } + @inlinable public init(unsafe value: consuming Value) { self.value = value } - /// Creates a new instance by throwing the `error()` on failure. - /// - /// - Requires: `value ∈ 0 up to Target.size` - /// - @inlinable public init(_ value: consuming Value, prune error: @autoclosure () -> Error) throws where Error: Swift.Error { - guard Self.predicate(value) else { throw error() } - self.value = value + @inlinable public consuming func payload() -> Value { + self.value } //=------------------------------------------------------------------------= diff --git a/Tests/UltimathnumTests/Guarantee.swift b/Tests/UltimathnumTests/Guarantee.swift new file mode 100644 index 00000000..078c7813 --- /dev/null +++ b/Tests/UltimathnumTests/Guarantee.swift @@ -0,0 +1,65 @@ +//=----------------------------------------------------------------------------= +// 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 RandomIntKit +import TestKit + +//*============================================================================* +// MARK: * Guarantee +//*============================================================================* + +final class GuaranteeTests: XCTestCase { + + //=------------------------------------------------------------------------= + // MARK: Tests x Random + //=------------------------------------------------------------------------= + + func testInitByFuzzing() { + func whereIs(_ type: T.Type, size: IX, rounds: IX, randomness: consuming FuzzerInt) where T: BinaryInteger { + func random() -> T { + let index = IX.random(in: 00000 ..< size, using: &randomness)! + let pattern = T.Signitude.random(through: Shift(Count(index)), using: &randomness) + return T(raw: pattern) // do not forget about infinite values! + } + + func check(_ value: G.Value, as guarantee: G.Type) where G: Guarantee, G.Value: Equatable { + if G.predicate(value) { + Test().same(G( value) .payload(), value) + Test().same(G(unchecked: value) .payload(), value) + Test().same(G(exactly: value)?.payload(), value) + Test().success(try G(value, prune: Bad.any).payload(), value) + } else { + Test().none(G(exactly: value)) + Test().failure(try G(value, prune: Bad.any), Bad.any) + } + } + + for _ in 0 ..< rounds { + let ((random)): T = random() + check(random, as: Finite .self) + check(random, as: Natural.self) + check(random, as: Nonzero.self) + check(Count(raw: IX(load: random)), as: Shift.self) + } + } + + for type in coreSystemsIntegers { + whereIs(type, size: IX(size: type), rounds: 16, randomness: fuzzer) + } + + whereIs(DoubleInt.self, size: 0016, rounds: 16, randomness: fuzzer) + whereIs(DoubleInt.self, size: 0016, rounds: 16, randomness: fuzzer) + + whereIs(InfiniInt.self, size: 0064, rounds: 16, randomness: fuzzer) + whereIs(InfiniInt.self, size: 0064, rounds: 16, randomness: fuzzer) + } +} diff --git a/Tests/UltimathnumTests/Guarantees/Finite.swift b/Tests/UltimathnumTests/Guarantees/Finite.swift index 93e7eeff..3a001b34 100644 --- a/Tests/UltimathnumTests/Guarantees/Finite.swift +++ b/Tests/UltimathnumTests/Guarantees/Finite.swift @@ -23,31 +23,29 @@ final class FiniteTests: XCTestCase { //=------------------------------------------------------------------------= func testForEachByteEntropyExtension() { - func whereTheValueIs(_ type: Value.Type) where Value: BinaryInteger { - typealias T = Finite + func whereIs(_ type: T.Type) where T: BinaryInteger { + typealias G = Finite - for x in (I8.min...I8.max).lazy.map(Value.init(load:)) { - if !x.isInfinite { - Test().same(T(x).magnitude().value, x.magnitude()) - Test().same(T(x).value, x) - Test().same(T(unchecked: x) .value, x) - Test().same(T(exactly: x)!.value, x) - Test().success(try T(x, prune: Bad.code123).value, x) - } else { - Test().none(T(exactly: x)) - Test().failure(try T(x, prune: Bad.code123).value, Bad.code123) + for pattern in I8.min...I8.max { + let value = T(load: pattern) + + Test().same(G.predicate(value), !value.isInfinite) + + if let guarantee = G(exactly: value) { + Test().same(guarantee.value, value) + Test().same(guarantee.magnitude().value, value.magnitude()) } } } for type in coreSystemsIntegers { - whereTheValueIs(type) + whereIs(type) } - whereTheValueIs(DoubleInt.self) - whereTheValueIs(DoubleInt.self) + whereIs(DoubleInt.self) + whereIs(DoubleInt.self) - whereTheValueIs(InfiniInt.self) - whereTheValueIs(InfiniInt.self) + whereIs(InfiniInt.self) + whereIs(InfiniInt.self) } } diff --git a/Tests/UltimathnumTests/Guarantees/Natural.swift b/Tests/UltimathnumTests/Guarantees/Natural.swift index 307bc058..80ccaef2 100644 --- a/Tests/UltimathnumTests/Guarantees/Natural.swift +++ b/Tests/UltimathnumTests/Guarantees/Natural.swift @@ -23,33 +23,29 @@ final class NaturalTests: XCTestCase { //=------------------------------------------------------------------------= func testForEachByteEntropyExtension() { - func whereTheValueIs(_ type: Value.Type) where Value: BinaryInteger { - typealias T = Natural - typealias S = Natural - typealias M = Natural + func whereIs(_ type: T.Type) where T: BinaryInteger { + typealias G = Natural - for x in (I8.min...I8.max).lazy.map(Value.init(load:)) { - if !x.isInfinite, !x.isNegative { - Test().same(T(x).magnitude().value, x.magnitude()) - Test().same(T(x).value, x) - Test().same(T(unchecked: x) .value, x) - Test().same(T(exactly: x)!.value, x) - Test().success(try T(x, prune: Bad.code123).value, x) - } else { - Test().none(T(exactly: x)) - Test().failure(try T(x, prune: Bad.code123).value, Bad.code123) + for pattern in I8.min...I8.max { + let value = T(load: pattern) + + Test().same(G.predicate(value), !value.isInfinite && !value.isNegative) + + if let guarantee = G(exactly: value) { + Test().same(guarantee.value, value) + Test().same(guarantee.magnitude().value, value.magnitude()) } } } for type in coreSystemsIntegers { - whereTheValueIs(type) + whereIs(type) } - whereTheValueIs(DoubleInt.self) - whereTheValueIs(DoubleInt.self) + whereIs(DoubleInt.self) + whereIs(DoubleInt.self) - whereTheValueIs(InfiniInt.self) - whereTheValueIs(InfiniInt.self) + whereIs(InfiniInt.self) + whereIs(InfiniInt.self) } } diff --git a/Tests/UltimathnumTests/Guarantees/Nonzero.swift b/Tests/UltimathnumTests/Guarantees/Nonzero.swift index f57302ac..63622da1 100644 --- a/Tests/UltimathnumTests/Guarantees/Nonzero.swift +++ b/Tests/UltimathnumTests/Guarantees/Nonzero.swift @@ -22,76 +22,33 @@ final class NonzeroTests: XCTestCase { // MARK: Tests //=------------------------------------------------------------------------= - func testBitCast() { - func whereTheValueIs(_ type: Value.Type) where Value: BinaryInteger { - typealias T = Nonzero - typealias S = Nonzero - typealias M = Nonzero + func testForEachByteEntropyExtension() { + func whereIs(_ type: T.Type) where T: BinaryInteger { + typealias G = Nonzero - Test().same(T(raw: M(~2)).value, ~2 as T.Value) - Test().same(T(raw: S(-2)).value, ~1 as T.Value) - Test().same(T(raw: M( 1)).value, 1 as T.Value) - Test().same(T(raw: S( 2)).value, 2 as T.Value) + for pattern in I8.min...I8.max { + let value = T(load: pattern) + + Test().same(G.predicate(value), !value.isZero) + + if let guarantee = G(exactly: value) { + Test().same(guarantee.value, value) + Test().same(guarantee.complement().value, value.complement()) + Test().same(guarantee.magnitude ().value, value.magnitude ()) + Test().same(G(raw: Nonzero(T.Signitude(raw: value))).value, value) + Test().same(G(raw: Nonzero(T.Magnitude(raw: value))).value, value) + } + } } for type in coreSystemsIntegers { - whereTheValueIs(type) + whereIs(type) } - whereTheValueIs(DoubleInt.self) - whereTheValueIs(DoubleInt.self) + whereIs(DoubleInt.self) + whereIs(DoubleInt.self) - whereTheValueIs(InfiniInt.self) - whereTheValueIs(InfiniInt.self) - } - - func testInitExactly() { - func whereTheValueIs(_ type: Value.Type) where Value: BinaryInteger { - typealias T = Nonzero - - Test().same(T(exactly: ~2)?.value, ~002) - Test().same(T(exactly: ~1)?.value, ~001) - Test().same(T(exactly: ~0)?.value, ~000) - Test().none(T(exactly: 0)) - Test().same(T(exactly: 1)?.value, 001) - Test().same(T(exactly: 2)?.value, 002) - Test().same(T(exactly: 3)?.value, 003) - } - - for type in coreSystemsIntegers { - whereTheValueIs(type) - } - - whereTheValueIs(DoubleInt.self) - whereTheValueIs(DoubleInt.self) - - whereTheValueIs(InfiniInt.self) - whereTheValueIs(InfiniInt.self) - } - - func testInitPrune() { - func whereTheValueIs(_ type: Value.Type) where Value: BinaryInteger { - typealias T = Nonzero - - Test().success(try T(~2, prune: Bad.code123).value, ~00000000002) - Test().success(try T(~1, prune: Bad.code456).value, ~00000000001) - Test().success(try T(~0, prune: Bad.code789).value, ~00000000000) - Test().failure(try T( 0, prune: Bad.code123).value, Bad.code123) - Test().failure(try T( 0, prune: Bad.code456).value, Bad.code456) - Test().failure(try T( 0, prune: Bad.code789).value, Bad.code789) - Test().success(try T( 1, prune: Bad.code123).value, 00000000001) - Test().success(try T( 2, prune: Bad.code456).value, 00000000002) - Test().success(try T( 3, prune: Bad.code789).value, 00000000003) - } - - for type in coreSystemsIntegers { - whereTheValueIs(type) - } - - whereTheValueIs(DoubleInt.self) - whereTheValueIs(DoubleInt.self) - - whereTheValueIs(InfiniInt.self) - whereTheValueIs(InfiniInt.self) + whereIs(InfiniInt.self) + whereIs(InfiniInt.self) } } diff --git a/Tests/UltimathnumTests/Guarantees/Shift.swift b/Tests/UltimathnumTests/Guarantees/Shift.swift index e1d26e94..00d1bdd0 100644 --- a/Tests/UltimathnumTests/Guarantees/Shift.swift +++ b/Tests/UltimathnumTests/Guarantees/Shift.swift @@ -22,7 +22,7 @@ final class ShiftTests: XCTestCase { // MARK: Tests //=------------------------------------------------------------------------= - func testStaticInstances() { + func testInstances() { func whereTheValueIs(_ type: Value.Type) where Value: UnsignedInteger { typealias T = Shift @@ -42,80 +42,42 @@ final class ShiftTests: XCTestCase { whereTheValueIs(InfiniInt.self) } - func testInitExactly() { - func whereTheValueIs(_ type: Value.Type) where Value: UnsignedInteger { - typealias T = Shift - - always: do { - Test().same(T(exactly: Count(raw: 0 as IX))?.value, Count(0)) - Test().same(T(exactly: Count(raw: 1 as IX))?.value, Count(1)) - Test().same(T(exactly: Count(raw: 2 as IX))?.value, Count(2)) - } - - if !Value.size.isInfinite { - Test().none(T(exactly: Count(raw: ~2 as IX))) - Test().none(T(exactly: Count(raw: ~1 as IX))) - Test().none(T(exactly: Count(raw: ~0 as IX))) - } else { - Test().same(T(exactly: Count(raw: ~2 as IX))?.value, Count(raw: ~2 as IX)) - Test().same(T(exactly: Count(raw: ~1 as IX))?.value, Count(raw: ~1 as IX)) - Test().none(T(exactly: Count(raw: ~0 as IX))) - } - - if let size: IX = Value.size.natural().optional() { - Test().same(T(exactly: Count(size - 1))?.value, Count(size - 1)) - Test().none(T(exactly: Count(size ))) - Test().none(T(exactly: Count(size + 1))) - } - } - - for type in coreSystemsIntegersWhereIsUnsigned { - whereTheValueIs(type) - } - - whereTheValueIs(DoubleInt.self) - whereTheValueIs(DoubleInt.self) - - whereTheValueIs(InfiniInt.self) - whereTheValueIs(InfiniInt.self) - } - - func testInitPrune() { - func whereTheValueIs(_ type: Value.Type) where Value: UnsignedInteger { - typealias T = Shift + func testPredicate() { + func whereIs(_ type: T.Type) where T: UnsignedInteger { + typealias G = Shift always: do { - Test().success(try T(Count(raw: 0 as IX), prune: Bad.code123).value, Count(0 as IX)) - Test().success(try T(Count(raw: 1 as IX), prune: Bad.code456).value, Count(1 as IX)) - Test().success(try T(Count(raw: 2 as IX), prune: Bad.code789).value, Count(2 as IX)) + Test().yay(G.predicate(Count(raw: 0 as IX))) + Test().yay(G.predicate(Count(raw: 1 as IX))) + Test().yay(G.predicate(Count(raw: 2 as IX))) } - if !Value.size.isInfinite { - Test().failure(try T(Count(raw: ~2 as IX), prune: Bad.code123), Bad.code123) - Test().failure(try T(Count(raw: ~1 as IX), prune: Bad.code456), Bad.code456) - Test().failure(try T(Count(raw: ~0 as IX), prune: Bad.code789), Bad.code789) + if !T.size.isInfinite { + Test().nay(G.predicate(Count(raw: ~2 as IX))) + Test().nay(G.predicate(Count(raw: ~1 as IX))) + Test().nay(G.predicate(Count(raw: ~0 as IX))) } else { - Test().success(try T(Count(raw: ~2 as IX), prune: Bad.code123).value, Count(raw: ~2 as IX)) - Test().success(try T(Count(raw: ~1 as IX), prune: Bad.code456).value, Count(raw: ~1 as IX)) - Test().failure(try T(Count(raw: ~0 as IX), prune: Bad.code789), Bad.code789) + Test().yay(G.predicate(Count(raw: ~2 as IX))) + Test().yay(G.predicate(Count(raw: ~1 as IX))) + Test().nay(G.predicate(Count(raw: ~0 as IX))) } - if let size: IX = Value.size.natural().optional() { - Test().success(try T(Count(size - 1), prune: Bad.code123).value, Count(size - 1)) - Test().failure(try T(Count(size ), prune: Bad.code456), Bad.code456) - Test().failure(try T(Count(size + 1), prune: Bad.code789), Bad.code789) + if let size: IX = T.size.natural().optional() { + Test().yay(G.predicate(Count(size - 1))) + Test().nay(G.predicate(Count(size ))) + Test().nay(G.predicate(Count(size + 1))) } } for type in coreSystemsIntegersWhereIsUnsigned { - whereTheValueIs(type) + whereIs(type) } - whereTheValueIs(DoubleInt.self) - whereTheValueIs(DoubleInt.self) + whereIs(DoubleInt.self) + whereIs(DoubleInt.self) - whereTheValueIs(InfiniInt.self) - whereTheValueIs(InfiniInt.self) + whereIs(InfiniInt.self) + whereIs(InfiniInt.self) } //=------------------------------------------------------------------------=