Skip to content

Commit

Permalink
Protocol: Guarantee (#62).
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Aug 7, 2024
1 parent 9527edd commit d8d92b4
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 161 deletions.
48 changes: 48 additions & 0 deletions Sources/CoreKit/Guarantee+Validation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//=----------------------------------------------------------------------------=
// 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: `Self.predicate(value)` must be `true` to succeed.
///
@inlinable public init(_ value: consuming Value) {
self.init(exactly: value)!
}

/// Creates a new instance by returning `nil` on failure.
///
/// - Requires: `Self.predicate(value)` must be `true` to succeed.
///
@inlinable public init?(exactly value: consuming Value) {
guard Self.predicate(value) else { return nil }
self.init(unchecked: value)
}

/// Creates a new instance by throwing the `error()` on failure.
///
/// - Requires: `Self.predicate(value)` must be `true` to succeed.
///
@inlinable public init<Failure>(
_ value: consuming Value,
prune error: @autoclosure () -> Failure
) throws where Failure: Swift.Error {
guard Self.predicate(value) else { throw error() }
self.init(unchecked: value)
}
}
45 changes: 45 additions & 0 deletions Sources/CoreKit/Guarantee.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//=----------------------------------------------------------------------------=
// 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.
public protocol Guarantee<Value> {

associatedtype Value

//=------------------------------------------------------------------------=
// MARK: Metadata
//=------------------------------------------------------------------------=

/// Indicates whether the given `value` can be trusted.
@inlinable static func predicate(_ value: borrowing Value) -> Bool

//=------------------------------------------------------------------------=
// MARK: Initializers
//=------------------------------------------------------------------------=

/// Creates a new instance without validation in release mode.
///
/// - Requires: `Self.predicate(value)` must be `true` to succeed.
///
/// - Warning: Only use this method when you know the `value` is valid.
///
@_disfavoredOverload // collection.map(Self.init)
@inlinable init(unchecked value: consuming Value)

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

/// The value of this trusted input.
@inlinable var value: Value { get }
}
41 changes: 2 additions & 39 deletions Sources/CoreKit/Models/Guarantees/Finite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
/// init(unchecked:) // error: unchecked
/// ```
///
@frozen public struct Finite<Value>: Equatable where Value: BinaryInteger {
@frozen public struct Finite<Value>: Equatable, Guarantee where Value: BinaryInteger {

public typealias Value = Value

//=------------------------------------------------------------------------=
// MARK: Metadata
//=------------------------------------------------------------------------=
Expand All @@ -46,47 +44,12 @@
// MARK: Initializers
//=------------------------------------------------------------------------=

/// Creates a new instance without precondition checks.
///
/// - Requires: `!value.isInfinite`
///
/// - Warning: Use this method only when you are 100% sure the input is valid.
///
@_disfavoredOverload // enables: elements.map(Self.init)
@_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.isInfinite`
///
@inlinable public init(_ value: consuming Value) {
self.init(exactly: value)!
}

/// Creates a new instance by returning `nil` on failure.
///
/// - Requires: `!value.isInfinite`
///
@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.isInfinite`
///
@inlinable public init<Failure>(
_ value: consuming Value,
prune error: @autoclosure () -> Failure
) throws where Failure: Swift.Error {
guard Self.predicate(value) else { throw error() }
self.value = value
}

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=
Expand Down
39 changes: 1 addition & 38 deletions Sources/CoreKit/Models/Guarantees/Natural.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@
/// 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<Value>: Equatable where Value: BinaryInteger {

public typealias Value = Value
@frozen public struct Natural<Value>: Equatable, Guarantee where Value: BinaryInteger {

//=------------------------------------------------------------------------=
// MARK: Metadata
Expand All @@ -53,47 +51,12 @@
// MARK: Initializers
//=------------------------------------------------------------------------=

/// Creates a new instance without precondition checks.
///
/// - Requires: `value.appendix == 0`
///
/// - Warning: Use this method only when you are 100% sure the input is valid.
///
@_disfavoredOverload // enables: elements.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.appendix == 0`
///
@inlinable public init(_ value: consuming Value) {
self.init(exactly: value)!
}

/// Creates a new instance by returning `nil` on failure.
///
/// - Requires: `value.appendix == 0`
///
@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.appendix == 0`
///
@inlinable public init<Failure>(
_ value: consuming Value,
prune error: @autoclosure () -> Failure
) throws where Failure: Swift.Error {
guard Self.predicate(value) else { throw error() }
self.value = value
}

//=------------------------------------------------------------------------=
// MARK: Transformations
//=------------------------------------------------------------------------=
Expand Down
41 changes: 2 additions & 39 deletions Sources/CoreKit/Models/Guarantees/Nonzero.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@
/// init(unchecked:) // error: unchecked
/// ```
///
@frozen public struct Nonzero<Value>: BitCastable, Equatable where Value: BinaryInteger {

public typealias Value = Value
@frozen public struct Nonzero<Value>: BitCastable, Equatable, Guarantee where Value: BinaryInteger {

public typealias BitPattern = Nonzero<Value.Magnitude>

Expand All @@ -48,47 +46,12 @@
// MARK: Initializers
//=------------------------------------------------------------------------=

/// Creates a new instance without precondition checks.
///
/// - Requires: `value != 0`
///
/// - Warning: Use this method only when you are 100% sure the input is valid.
///
@_disfavoredOverload // enables: elements.map(Self.init)
@_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) {
self.init(exactly: 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 }
self.value = value
}

/// Creates a new instance by throwing the `error()` on failure.
///
/// - Requires: `value != 0`
///
@inlinable public init<Failure>(
_ value: consuming Value,
prune error: @autoclosure () -> Failure
) throws where Failure: Swift.Error {
guard Self.predicate(value) else { throw error() }
self.value = value
}

//=------------------------------------------------------------------------=
// MARK: Initializers
//=------------------------------------------------------------------------=
Expand Down
41 changes: 3 additions & 38 deletions Sources/CoreKit/Models/Guarantees/Shift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@
/// init(unchecked:) // error: unchecked
/// ```
///
@frozen public struct Shift<Target>: Equatable where Target: UnsignedInteger {
@frozen public struct Shift<Target>: Equatable, Guarantee where Target: UnsignedInteger {

public typealias Target = Target

public typealias Value = Count<IX>

//=------------------------------------------------------------------------=
// MARK: Metadata
//=------------------------------------------------------------------------=
Expand Down Expand Up @@ -77,47 +77,12 @@
// MARK: Initializers
//=------------------------------------------------------------------------=

/// Creates a new instance without precondition checks.
///
/// - Requires: `value < Target.size`
///
/// - Warning: Use this method only when you are 100% sure the input is valid.
///
@_disfavoredOverload // enables: elements.map(Self.init)
@_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 < Target.size`
///
@inlinable public init(_ value: consuming Value) {
self.init(exactly: value)!
}

/// Creates a new instance by returning `nil` on failure.
///
/// - Requires: `value < Target.size`
///
@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 < Target.size`
///
@inlinable public init<Failure>(
_ value: consuming Value,
prune error: @autoclosure () -> Failure
) throws where Failure: Swift.Error {
guard Self.predicate(value) else { throw error() }
self.value = value
}

//=------------------------------------------------------------------------=
// MARK: Utilities
//=------------------------------------------------------------------------=
Expand Down
14 changes: 7 additions & 7 deletions Tests/CoreKitTests/Divider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ final class DividerTests: XCTestCase {
//=------------------------------------------------------------------------=

func testInstances() {
func check<T>(_ divisor: T, mul: T, add: T, shr: T) where T: SystemsInteger & UnsignedInteger {
func check<T>(_ divisor: T, mul: T, add: T, shr: T, line: UInt = #line) where T: SystemsInteger & UnsignedInteger {
let divider = Divider(exactly: divisor)!
Test().same(divider.multiplier, mul)
Test().same(divider.increment, add)
Test().same(divider.shift, shr)
Test(line: line).same(divider.multiplier, mul)
Test(line: line).same(divider.increment, add)
Test(line: line).same(divider.shift, shr)
}

for distance: IX in 0 ..< 8 {
Expand All @@ -40,7 +40,7 @@ final class DividerTests: XCTestCase {
check(07 as U16, mul: 00000000000000037449, add: 00000000000000037449, shr: 16 + 2) // shr: 18
check(07 as U32, mul: 00000000002454267026, add: 00000000002454267026, shr: 32 + 2) // shr: 34
check(07 as U64, mul: 10540996613548315209, add: 10540996613548315209, shr: 64 + 2) // shr: 66

check(10 as U8, mul: 00000000000000000205, add: 00000000000000000000, shr: 08 + 3) // shr: 11
check(10 as U16, mul: 00000000000000052429, add: 00000000000000000000, shr: 16 + 3) // shr: 19
check(10 as U32, mul: 00000000003435973837, add: 00000000000000000000, shr: 32 + 3) // shr: 35
Expand Down Expand Up @@ -118,9 +118,9 @@ final class DividerTests: XCTestCase {
}

for _ in 0 ..< rounds {
let divider = Divider(Swift.max(1, random()))
guard let divider = Divider(exactly: random()) else { continue }
let dividend: T = random()
let expectation = dividend.division(Nonzero(divider.divisor)).unwrap()
let expectation = dividend.division(Nonzero(divider.divisor)) as Division
Test().same(dividend.division(divider), expectation)
Test().same(divider .division(dividing: dividend), expectation)
Test().same(dividend.quotient(divider), expectation.quotient)
Expand Down

0 comments on commit d8d92b4

Please sign in to comment.