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 like ff/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#NMeaning
<Integer> + <Integer>40addition
<Integer> - <Integer>40subtraction
<Integer> * <Integer>50multiplication
<Integer> / <Integer>50integer division
- <Integer>80unary negation
| <Integer> |35absolute 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#NNative / definition
<Integer> > <Integer>30reduces to gt (native)
<Integer> < <Integer>30defined in integers.nl
<Integer> <= <Integer>30defined in integers.nl
<Integer> >= <Integer>30defined 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:

KindNames
TypeInteger
Literal<NUMBER>
Operators+, - (binary and unary), *, /, |x|, <, <=, >, >=

add, mult, and gt are private and are not visible to importers.


See also