await Sliced Bread 3.0 #120
Replies: 49 comments 59 replies
-
I will take a while until it becomes benchmark-able, but the API is already pure greatness 🚀
|
Beta Was this translation helpful? Give feedback.
-
I'll add two 1-bit integers too so double-and-add algorithms just work: for bit: BitInt.Magnitude in ChunkedInt(normalizing: base, isSigned: false).reversed() {
double()
if bit == 1 {
increment()
}
} |
Beta Was this translation helpful? Give feedback.
-
The new protocol hierarchy is fully abstract, which is really quite neat; it doesn't assume any integer type. Additionally, the requirements are so few that they fit on my 13-inch MacBook screen ⭐ 💻 ⭐ |
Beta Was this translation helpful? Give feedback.
-
Honorable mentions: |
Beta Was this translation helpful? Give feedback.
-
Here's an example of code that's trivial to write with error propagation: if let shift = try? shift.incremented(by: bitWidth) {
XCTAssertEqual(operand &<< shift, result, file: file, line: line)
}
if let shift = try? shift.incremented(by: bitWidth).incremented(by: bitWidth) {
XCTAssertEqual(operand &<< shift, result, file: file, line: line)
}
if let shift = try? shift.decremented(by: bitWidth) {
XCTAssertEqual(operand &<< shift, result, file: file, line: line)
}
if let shift = try? shift.decremented(by: bitWidth).decremented(by: bitWidth) {
XCTAssertEqual(operand &<< shift, result, file: file, line: line)
} Compare that to the following (another take on the second assertion): attempt: do {
var help = (partialValue: shift, overflow: false)
help = help.partialValue.addingReportingOverflow(bitWidth)
guard !help.overflow else { break attempt }
help = help.partialValue.addingReportingOverflow(bitWidth)
guard !help.overflow else { break attempt }
XCTAssertEqual(operand &<< help.partialValue, result, file: file, line: line)
} Alternatively, I could return a struct with convenience methods: if let shift = shift.incremented(by: bitWidth).optional()?.incremented(by: bitWidth).optional() {
XCTAssertEqual(operand &<< shift, result, file: file, line: line)
}
if let shift = try? shift.incremented(by: bitWidth).throwOnOverflow().incremented(by: bitWidth).throwOnOverflow() {
XCTAssertEqual(operand &<< shift, result, file: file, line: line)
} |
Beta Was this translation helpful? Give feedback.
-
Speaking of structs. I find it common to round up division. I could do: try dividend.divided(by: divisor) // quotient, remainder
try dividend.divided(by: divisor).ceil() // quotient + (remainder > 0 ? 1 : 0) |
Beta Was this translation helpful? Give feedback.
-
I might need a failable version of init(integerLiteral:) to make the Fibonacci sequence work with BitInt and BitInt.Magnitude. Can't easily double() the index otherwise. These 1-bit integers can represent 0 and 2 sequence pairs. Have fun 🤣 |
Beta Was this translation helpful? Give feedback.
-
I might make the big integer generic, because I want simple dependencies. The new CoreKit doesn't even have an integer type. Lulz. |
Beta Was this translation helpful? Give feedback.
-
You could even write generic collections without concrete integer types if collections looked like the following: protocol Collection {
associatedtype Stride:
SignedInteger & SystemInteger where
Stride.BitPattern == Word
var count: Stride { get }
} I don't want to rewrite the collection protocol hierarchy, so I might pretend that's what I've done whenever I use Int or UInt in a generic collection. Imagine these types being Stride or Stride.Magnitude instead. |
Beta Was this translation helpful? Give feedback.
-
Hm. I thought it would be clever to give every integer a negate() method and use that to recover the magnitude of unsigned binary integer subtraction overflow. After testing it, however, it seems like that's not possible and that I need to use a twosComplement() operation instead. This is because one operation needs to be extending and one needs to be truncating, if that makes sense. |
Beta Was this translation helpful? Give feedback.
-
Hm. Is there a better normalization mode for unsigned integers? Like, can we pretend UIntXL is truly infinite-width and NOT(x) to infinity? That would make NOT(x) and twos complements reversible, at least. |
Beta Was this translation helpful? Give feedback.
-
Unsigned big (normalized) binary integers are a bit handicapped and the question is whether the protocol should share its limitations. I could drop bit inversions, I could drop the protocol conformance, or I could make a signed big (normalized) binary integer instead. Hm. |
Beta Was this translation helpful? Give feedback.
-
Hm. If a big unsigned binary integer can represent infinity (all 1s), then both ~x and negated() would be recoverable. In that case, I could even add a static bit width to every binary integer type: protocol BinaryInteger {
static var bitWidth: Magnitude { get }
}
BigInt.bitWidth // BigInt.Magnitude.infinity |
Beta Was this translation helpful? Give feedback.
-
Hm. Unsigned infinite-width integers are such a headache compared to signed ones. Signed big binary integers are always well-behaved, whereas I'm not sure you can make an unsigned big integer that is recoverable and consistent. The problem is really the bitwise not (~) operation, which is either truncating and non-recoverable or extending and infinity-weird. |
Beta Was this translation helpful? Give feedback.
-
Hm. A core module without a currency integer type might be a bit too anti-pragmatic. I might change that. |
Beta Was this translation helpful? Give feedback.
-
Hm. I think there might be like two |
Beta Was this translation helpful? Give feedback.
-
Here's a cool piece of code: var value = I8(0)
let error = value[raw: U8.self][{ $0.decremented() }]
print(value) // -1
print(error) // true
The 2nd subscript cuts the project code in half, or some such silly amount. Also, I've renamed |
Beta Was this translation helpful? Give feedback.
-
I'm thinking of reimplementing the SignedInt<Magnitude> model as a proof-of-it-works. Having said that, I wonder if I should ban negative infinities. I have a sign-and-magnitude coder at the moment, which allows negative infinities as a consequence of not disallowing it, but I think there's at least some merit in having a format that cannot represent binary integers that are too negative to store in memory. If I ban it, then infinity could be in the same regex group as "+" and "-". |
Beta Was this translation helpful? Give feedback.
-
Ooo, I just added a 3rd flavor of binary integer. I wonder what it could be? 👀 |
Beta Was this translation helpful? Give feedback.
-
The 🧗 to 💯 unit test code coverage has begun 😣 |
Beta Was this translation helpful? Give feedback.
-
Hm. I have a single custom collection. It's really convenient, but I'm also tempted to remove it. Keep it simple. |
Beta Was this translation helpful? Give feedback.
-
I was bored so I added these to the new and improved Fibonacci<T> sequence: /// Forms the sequence pair at `index + other.index`.
public mutating func increment(by other: Self) throws
/// Forms the sequence pair at `index - other.index`.
public mutating func decrement(by other: Self) throws /// Generates new instances and uses them to check math invariants.
///
/// ### Invariants
///
/// ```
/// f(x) * f(y) == (f(x+y+1) / f(x+1) - f(y+1)) * f(x+1) + f(x+y+1) % f(x+1)
/// ```
///
/// ### Calls: Fibonacci
///
/// - Fibonacci.init(\_:)
/// - Fibonacci/increment(by:)
/// - Fibonacci/decrement(by:)
///
/// ### Calls: BinaryInteger
///
/// - BinaryInteger/plus(\_:)
/// - BinaryInteger/minus(\_:)
/// - BinaryInteger/times(\_:)
/// - BinaryInteger/quotient(\_:)
/// - BinaryInteger/division(\_:)
///
func checkMathInvariants() {
typealias Bad = FibonacciTests.Failure
for divisor: Divisor<Value> in [2, 3, 5, 7, 11].map(Divisor.init) {
always: do {
var a = self.item as Fibonacci<Value>
let b = try Fibonacci(a.index.quotient(divisor).prune(Bad.division))
let c = try a.next.division(Divisor(b.next, prune:Bad.divisor)).prune(Bad.division)
try a.decrement(by: b)
let d = try b.element.times(a.element).prune(Bad.multiplication)
let e = try c.quotient.minus(a.next).times(b.next).plus(c.remainder).prune(Bad.any)
try a.increment(by: b)
test.same(d, e, "arithmetic invariant error")
self.same(index: a.index, element: a.element, next: a.next)
} catch let error {
test.fail("unexpected arithmetic failure: \(error)")
}
}
} |
Beta Was this translation helpful? Give feedback.
-
try index.minus(1).veto({ $0.isNegative }).prune(Failure.overflow) |
Beta Was this translation helpful? Give feedback.
-
God, save me from writing READMEs 😣 |
Beta Was this translation helpful? Give feedback.
-
These are the protocols I have at the moment:
Maybe I'll add endianness to the mix, dunno. |
Beta Was this translation helpful? Give feedback.
-
I'm now convinced that these two subscripts are anti-patterns, so I spent today removing them and all uses of them. The subscript(raw:) is too niche to consider on its own. The closure-based subscript(_:) is the bad one, really. It's such a good stop-gap that it hid the fact that I should have added some more extensions to improve all of the most common code patterns instead. |
Beta Was this translation helpful? Give feedback.
-
Also, I just noticed that x-by-y addition should take an unsigned increment. This is because the bit pattern extension of signed increments negates the benefit of having a separate algorithm. I suppose I did not notice it before because 99% of all such calls are made by unsigned wide integers. Anyhow, that will be fixed. |
Beta Was this translation helpful? Give feedback.
-
It turns out that Fallible<Void> is a neat error indicator. It's a Bool with ergonomic features: data.increment(by: 1).unwrap() // Fallible<Void> -> Void + precondition(!error) |
Beta Was this translation helpful? Give feedback.
-
Made a division test case super generic for no reason ™️ and found a bug in my new Karatsuba algorithm. Love it and hate it. |
Beta Was this translation helpful? Give feedback.
-
The new project is public now, at least in some kind of alpha state. It's called: Ultimathnum. |
Beta Was this translation helpful? Give feedback.
-
I've got an awesome idea. It's Sliced Bread 3.0. I'll put this project on hold while I work on it. It's a simpler design, but I need to know whether it's viable w.r.t. performance, etc. It's a reduced instruction set that leverages some of Swift's new and upcoming features 🤩
Beta Was this translation helpful? Give feedback.
All reactions