nelumbo.integers
June 23, 2026 · View on GitHub
Arbitrary-precision integer arithmetic and comparison.
Source: src/main/resources/org/modelingvalue/nelumbo/integers/integers.nl — 38 lines.
Import:
import nelumbo.integers
nelumbo.integers imports nelumbo.logic, so the Boolean type, connectives, and equality come along automatically — there is no need to import logic separately.
Type
Integer :: Object
A value of type Integer is an arbitrary-precision signed integer. There is no overflow.
Literals
pattern RADIX_NUMBER ::= <(> <(> <NUMBER> <|> <NAME> <)> <)+>
Integer ::= <(> - <)?> <[> <NUMBER> <(> "#" <RADIX_NUMBER> <)?> <]>
@nelumbo.integers.NInteger
<NUMBER> is the language-level token defined in lang.nl as the unsigned digit run [0-9]+. The sign and the optional base form are built around it at the pattern level, not by the lexer. RADIX_NUMBER is a named pattern — a reusable abbreviation for the base-N digit run, kept separate so the Integer literal reads cleanly. The literal admits:
- ordinary signed decimals:
0,42,-1 - base-N literals:
<digits>#<digits-in-base>, where the leading number is the base — e.g.,16#ff,36#abc. The digits-in-base (RADIX_NUMBER) are themselves a repetition of<NUMBER>/<NAME>tokens, which is why letters likeff/abc(lexed as<NAME>) compose with the leading base.
The native class NInteger reassembles the matched parts, applies the leading sign, and parses the result into a BigInteger-backed value.
Arithmetic
Integer ::= <Integer> - <Integer> #40,
<Integer> + <Integer> #40,
- <Integer> #80,
<Integer> * <Integer> #50,
<Integer> / <Integer> #50,
| <Integer> | #35
| Pattern | #N | Meaning |
|---|---|---|
<Integer> + <Integer> | 40 | addition |
<Integer> - <Integer> | 40 | subtraction |
<Integer> * <Integer> | 50 | multiplication |
<Integer> / <Integer> | 50 | integer division |
- <Integer> | 80 | unary negation |
| <Integer> | | 35 | absolute value |
Six patterns, two native arithmetic primitives. The four binary operators reduce to add or mult — both @NelumboMethods on the single nelumbo.integers.Integers class (which also carries the gt comparison primitive below):
private Boolean ::= add(<Integer>,<Integer>,<Integer>) @nelumbo.integers.Integers,
mult(<Integer>,<Integer>,<Integer>) @nelumbo.integers.Integers,
gt(<Integer>,<Integer>) @nelumbo.integers.Integers
Integer a, b, c
a + b = c <=> add(a, b, c)
a - b = c <=> add(c, b, a)
a * b = c <=> mult(a, b, c)
a / b = c <=> mult(c, b, a)
Subtraction is not a separate native. It is add viewed from a different angle: a - b = c is the same proposition as c + b = a. The same trick gives integer division as mult(c, b, a).
Unary negation and absolute value are defined on top of subtraction:
- a = b <=> 0 - a = b
|a| = b <=> b = a if a >= 0,
b = -a if a < 0
The two guarded clauses of |a| cover the integer domain without overlap.
Bidirectional evaluation
Because add and mult are relational, any one of the three operands can be the unknown. From integersTest.nl:
10 + 11 = a ? [(a=21)][..]
a + 11 = 21 ? [(a=10)][..]
10 + a = 21 ? [(a=11)][..]
10 - 11 = a ? [(a=-1)][..]
a - 11 = -1 ? [(a=10)][..]
10 - a = -1 ? [(a=11)][..]
|a| = 10 ? [(a=-10),(a=10)][(a=0),..]
|10| = a ? [(a=10)][..]
Absolute value with the result fixed returns both pre-images on the facts side and lists a=0 as a proven falsehood (with .. for everything else).
Integer division
Integer division truncates toward zero. A query with a non-exact dividend gets an empty facts side — no integer makes the equation true:
20 / 10 = 2 ? [()][]
20 / 10 = 3 ? [][()]
21 / 10 = a ? [][..]
21 / 10 = 2 ? [][()]
Comparison
Boolean ::= <Integer> ">" <Integer> #30,
<Integer> "<" <Integer> #30,
<Integer> "<=" <Integer> #30,
<Integer> ">=" <Integer> #30
| Pattern | #N | Native / definition |
|---|---|---|
<Integer> > <Integer> | 30 | reduces to gt (native) |
<Integer> < <Integer> | 30 | defined in integers.nl |
<Integer> <= <Integer> | 30 | defined in integers.nl |
<Integer> >= <Integer> | 30 | defined in integers.nl |
The comparison operators themselves carry no @ binding — the single native comparison is the private helper gt, and the operators reduce to it and to =:
a > b <=> gt(a, b)
a < b <=> gt(b, a)
a <= b <=> a < b | a = b
a >= b <=> a > b | a = b
gt is native (rather than binding > directly) because an operator functor's name cannot bind a @NelumboMethod; routing through a named helper keeps the logic in a method. See native-cookbook.md.
Comparisons participate in three-valued classification. Asking a > 0 with a unbound does not enumerate the positive integers, but it does place a = 0 on the correct side:
a > 0 ? [..][(a=0),..] // (a=0) is a proven falsehood of a>0
a >= 0 ? [(a=0),..][..] // (a=0) is a proven fact of a>=0
All four comparison operators are quoted in the source (">", "<", "<=", ">=") because < and > are also the syntax markers that open and close pattern holes (<Integer>); the quotes tell the tokenizer to treat them as ordinary operator text.
Exports summary
Added to what nelumbo.logic already exports:
| Kind | Names |
|---|---|
| Type | Integer |
| Literal | <NUMBER> |
| Operators | +, - (binary and unary), *, /, |x|, <, <=, >, >= |
add, mult, and gt are private and are not visible to importers.
See also
logic.md— the moduleintegersbuilds onrationals.md— the same shape, lifted to exact rationalswriting-rules.md— how thea-b=c <=> add(c,b,a)idiom worksintegersTest.nl— executable specificationfibonacci.nl— non-trivial use of+,-, and<=