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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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