oscbyspro / Ultimathnum

Binary arithmetic reimagined in Swift
Apache License 2.0
9 stars 1 forks source link

Count as BinaryInteger-esque #107

Closed oscbyspro closed 1 month ago

oscbyspro commented 1 month ago

I've tried out various forms of Guarantee protocols, and there's no reasonable way to conform Count to any of them. So, I've concluded that it's better to shape it in the likeness of BinaryInteger instead. Its validation function should therefore be (#95):

Count.exactly(some BinaryInteger) -> Optional<Fallible<Count>>

Changes

I'll remove Count.init(exactly:), etc, and add a few methods similar to this:

extension Count {

    /// Loads the `source` and returns an `error` indicator, or `nil`.
    ///
    /// - Note: It is `nil` if `source` is not in  `[-IX.max, IX.max]`.
    ///
    /// - Note: The `error` is set if `source` is not in `[0, IX.max]`.
    ///
    /// - Note: `Natural<IX>` guarantees exact results.
    ///
    /// ### Examples
    ///
    ///     ┌──────────── → ──────────────────────────┬───────┐
    ///     │ source      │ value                     │ error │
    ///     ├──────────── → ──────────────────────────┼───────┤
    ///     │ IX.max  + 2 │ ------------------------- │ ----- │
    ///     │ IX.max  + 1 │ ------------------------- │ ----- │
    ///     │ IX.max      │ IX.max                    │ false │
    ///     │ IX.max  - 1 │ IX.max  - 1               │ false │
    ///     │ IX.max  - 2 │ IX.max  - 2               │ false │
    ///     ├─────────── → ───────────────────────────┼───────┤
    ///     │ IX.zero + 2 │ IX.zero + 2               │ false │
    ///     │ IX.zero + 1 │ IX.zero + 1               │ false │
    ///     │ IX.zero     │ IX.zero                   │ false │
    ///     │ IX.zero - 1 │ log2(&0 + 1) - 1          │ true  │
    ///     │ IX.zero - 2 │ log2(&0 + 1) - 2          │ true  │
    ///     ├──────────── → ──────────────────────────┼───────┤
    ///     │ IX.min  + 2 │ log2(&0 + 1) - IX.max - 1 │ true  │
    ///     │ IX.min  + 1 │ log2(&0 + 1) - IX.max     │ true  │
    ///     │ IX.min      │ ------------------------- │ ----- │
    ///     │ IX.min  - 1 │ ------------------------- │ ----- │
    ///     │ IX.min  - 2 │ ------------------------- │ ----- │
    ///     └──────────── → ──────────────────────────┴───────┘
    ///
    /// - Note: Binary integers cannot represent values near `log2(&0+1)`.
    ///
    @inlinable public static func exactly(_ source: /*borrowing*/ some BinaryInteger) -> Optional<Fallible<Self>> {
        IX.exactly(source).optional().flatMap(Self.exactly)
    }
}

Alternatives

The only other A-> Optional<Fallible<B>> conversion is BinaryInteger.leniently(some Swift.BinaryFloatingPoint), so I might consider using that signature and letting exactly(_:) return Optional<Self> instead. The problem is that the proposed version has the exact same semantics as BinaryInteger.exactly(_:) for all non-nil values.

oscbyspro commented 1 month ago

The negative truncation behavior assumes Count.infinity: log2(&0+1) is a power of 2. That's fine, and perhaps even desirable, since that makes every binary integer a power of 2. I suppose I'll take a moment to consider if this log2(&0+1) definition has any obviously negative consequences. Edit: It looks like I've already documented this binary integer size-is-power-of-2 definition. I'm not surprised because it makes infinite sense. That's the reason Count is 1's-complement-y. I need a vacation.

oscbyspro commented 1 month ago

Question: Why does Count.exactly(IX.min) return nil?

Answer: Because bit patterns in the finite-to-infinite gap don't preserve BinaryInteger.exactly(_:) semantics.

oscbyspro commented 1 month ago

Note that Count can't implement init(load:). It's neither a BinaryInteger nor a Guarantee but Count.exactly(_:) is more powerful than Count.init(exactly:) (and somewhat trickier to implement) so I'd argue its functionality is preferred. Edit: I suppose it could implement an optional init?(load:).

oscbyspro commented 1 month ago

I'll also add Count.init?(load: some BinaryInteger) because it makes the binary-integer-esque shape clearer.