Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Arithmetic

The usual signed-integer operations, all on i64, plus two helpers that show up often enough in penalty-weight and slack-variable arithmetic to earn their own opcodes: SQR, a one-instruction shorthand for COPY plus MUL, and BITLEN, which the VM has no other way to compute at all, since there is no logarithm instruction to compose it from. Byte values, operand layouts and stack effects are in the Arithmetic section of the opcode reference. None of these instructions touch a register; every operand comes off the stack and every result goes back onto it.

Overflow

Every arithmetic instruction on this page is checked. ADD, SUB, MUL, NEG, ABS, SQR, INC and DEC raise ArithmeticOverflow when the result would leave the i64 range, rather than wrapping to a value the program never asked for:

PUSH 9223372036854775807   ; i64::MAX
PUSH 1
ADD                        ; raises ArithmeticOverflow

The rule is on the result, not on the machine operation underneath it. ABS and NEG of i64::MIN raise, because no positive i64 has that magnitude. i64::MIN / -1 raises for the same reason. i64::MIN % -1 does not: the remainder is 0, which is perfectly representable, even though a fixed-width machine reaches it through an overflowing division.

Values built from several operations – a constraint expansion’s coefficients, an ENERGY accumulation – are checked at every step, so a computation that leaves the range on its way to an in-range answer raises rather than quietly recovering. Both reference interpreters implement the same rule, so a program that raises on one raises on the other.

Division and Remainder

DIV rounds toward negative infinity: it is floor division, matching Python’s //, not Rust’s default /, which truncates toward zero. MOD returns a remainder with the sign of the divisor, matching Python’s %, so the identity \(a = \lfloor a / b \rfloor \cdot b + (a \bmod b)\) holds under floored division. PUSH -7; PUSH 2; DIV pushes -4, not the -3 that truncating division would give, and PUSH -7; PUSH 2; MOD pushes 1, not -1. Both error with DivisionByZero when the divisor is zero rather than wrapping or producing a sentinel value: PUSH 5; PUSH 0; DIV halts execution with division by zero instead of continuing.

MIN, MAX and BITLEN

MIN and MAX are signed comparisons with no domain restriction. BITLEN pops a value and pushes the number of bits needed to represent it in binary, \(\lfloor\log_2(a)\rfloor + 1\), returning \(0\) for non-positive input rather than erroring (there is no bit length for a number that is not positive, and 0 is a more useful sentinel here than a fault, since BITLEN output typically feeds straight into SLACK’s capacity calculation). BITLEN(1) = 1, BITLEN(7) = 3, BITLEN(8) = 4, BITLEN(255) = 8.