02
Integers as differences¶
A pair of natural numbers (a, b) represents the difference
a - b. Equality must compare represented differences, not fields.
Natural numbers are immutable predecessor chains, and their equality and addition follow explicit zero/successor cases.
- explain why multiple pairs represent the same integer;
- derive the cross-sum equality rule;
- explain why
eq=Falseis necessary; - separate a representative from its normalized form.
Definition
Equal differences form an equivalence class¶
If (a, b) means a - b, then (3, 1) and (4, 2) both mean 2. Avoiding
subtraction gives a rule stated entirely with the natural-number addition we
already constructed:
This relation is reflexive, symmetric, and transitive, so each integer is an equivalence class of representatives. The library deliberately stores the chosen representative so that this construction remains visible.
Representation
@dataclass(frozen=True, slots=True, eq=False, repr=False)
class Integer:
a: NaturalNumber
b: NaturalNumber
def __post_init__(self) -> None:
if not isinstance(self.a, NaturalNumber) or not isinstance(
self.b, NaturalNumber
):
raise TypeError(
"Integer.a and Integer.b must be NaturalNumber values"
)
Generated field equality would make (3,1) != (4,2). eq=False prevents that
incorrect rule and leaves equality to the hand-written method.
Implementation
@log(log_level=11)
def __eq__(self, other: object) -> tuple[bool, LogMessage]:
converted = _coerce_integer(other)
if converted is None:
return cast(bool, NotImplemented), lambda: "NotImplemented"
result = self.a + converted.b == self.b + converted.a
return result, lambda: (
f"{self!r} == {converted!r} ⇔ "
f"{self.a!r} + {converted.b!r} == "
f"{self.b!r} + {converted.a!r}"
)
The line computing result is the defining equation with fields substituted
directly. _coerce_integer lets a natural number participate by embedding n
as (n, 0). An unrelated type returns NotImplemented and remains Python's
dispatch problem.
Addition is componentwise:
Multiplication follows expansion of (a-b)(c-d):
Trace
For (3,1) == (4,2), write the two natural-number sums that
Integer.__eq__ compares.
Compare the logged cross-sums with the definition.
At level 11 the trace emphasizes integer equality. Lowering the threshold to 4 also exposes the natural-number additions used to build the cross-sums; it does not change the answer.
Test
Tests compare unequal representatives of the same value and check that operations respect equality. Those are executable checks of well-definedness for selected inputs. The general claim still requires showing that changing representatives cannot change an operation's equivalence class.
Boundary
normalize() chooses (n,0) for nonnegative values or (0,n) for negative
values. Construction does not call it automatically. Keeping both (3,1) and
(4,2) intact is an educational choice, not a mathematical requirement.