Limb arrays: value semantics of the arithmetic kernels #
Every loop of Arithmetic.Runtime is specified by a segment invariant: processing the limbs
from index onward changes the segment starting at index by the intended amount and leaves the
limbs below index untouched. The invariants are proved by induction on the loop's remaining
count, and the toNat statements follow by splitting toNat into the segment below index, the
segment the loop wrote, and the untouched segment above it.
toNat_addWordAtandtoNat_addAt: adding a word at a limb or bit position, provided the result still fits the stored limbs;toNat_add:add a b carrydenotestoNat a + toNat b + carry, with no side condition because the result has one limb more than the wider operand;toNat_sub:sub a b borrowdenotestoNat a - toNat b - borrowwhenever that difference is nonnegative and the borrow is at most one;toNat_mul:mul a bdenotestoNat a * toNat b.
limb_setIfInBounds describes array updates; uint64_low_toNat and uint64_high_toNat
split each intermediate into a result limb and a carry.
Array updates and machine-word digits #
Carry propagation #
The carry loop adds its carry to the segment it processes and leaves the other limbs unchanged, provided the sum fits the processed limbs.
Addition #
Subtraction #
The subtraction loop writes the difference of the processed segments when it is nonnegative.
Multiplication #
A row of the schoolbook product adds m times a segment of b at its offset.
The row loop maintains the partial product and the zero limbs above it.