On the Most Suitable Axiomatization of Signed Integers

The standard mathematical deﬁnition of signed integers, based on set theory, is not well-adapted to the needs of computer science. For this reason, many formal speciﬁcation languages and theorem provers have designed alternative deﬁnitions of signed integers based on term algebras, by extending the Peano-style construction of unsigned naturals using “zero” and “succ” to the case of signed integers. We compare the various approaches used in CADP, CASL, Coq, Isabelle/HOL, KIV, Maude, mCRL2, PSF, SMT-LIB, TLA+, etc. according to objective criteria and suggest an “optimal” deﬁnition of signed integers.


Introduction
It took a few millennia to properly formalize number theory but, at present, mathematics has sound and well-established concepts for numbers.
In computer science, the situation is different.Following a tradition initiated by Fortran and Algol 60, most programming languages rely on a set of predefined data types, among which numerical types (integers, reals, etc.) have finite domains and usually map to the machine words provided by the underlying hardware.In many cases, the semantics of these types is not defined formally, as it depends on the implementation.Even languages with strong semantic foundations may be incompletely defined if they import predefined types; this is the case, for instance, with the synchronous languages Lustre [31,Sect. 3.1 and 3.2], Esterel [5,Sect. 4.3.1],and Signal [13,Sect. 2.3], which assume the existence of predefined signed integers and floating-point reals, presumably imported from the C language. 1 Some specification languages use a similar approach, by assuming the existence of numerical types rather than defining them formally.Because specification languages are expected to be more abstract and higher-level than programming languages, such predefined numerical types are usually infinite and the mapping to hardware implementation is often left aside.These are four examples of specification languages in which numbers are taken for granted1 : • The VDM language [15] [19, Sect.3.1.2]has five predefined numerical types, real numbers being the most general one (nat1 ⊂ nat ⊂ int ⊂ rat ⊂ real).
• The predefined library of PVS [25,Chap. 7] assumes the existence of a universal number type, of which the usual numerical types are subsets (naturalnumber ⊂ integer ⊂ rational ⊂ real ⊂ number).Actually, PVS defines many more types (e.g., posnat, nonneg int, nzrat, etc.) that form a lattice, the elements of which are related by PVS judgments and properties.
• The Z notation [16,Sect. 11.2 and B.7] assumes the existence of an unspecified number type A ("arithmos") that contains naturals and integers (N 1 ⊂ N ⊂ Z ⊂ A).These sets and their operations are defined using high-level statements such as: "multiplication on integers is characterised by the unique operation under which the integers become a commutative ring with identity element 1 " [16,Sect. B.7.11].
• The B language [20, Sect.3.4, 5.3, 5.6, and 7.25.2]assumes the existence of the set Z, of which N 1 and N are subsets (N 1 ⊂ N ⊂ Z).The language also defines a set INT of "concrete integers" that belong to a finite range MININT...MAXINT, the bounds of which are implementation-dependent, e.g., (−2 31 )...(2 31 − 1), as well as two subsets NAT 1 and NAT of INT without negative values (NAT Relying on undefined or externally-defined numerical types makes these languages closer to programming languages than formal methods.Their semantics is not entirely defined and properties involving numbers cannot be proven within these languages, unless some specific theories are imported.We believe that a unified semantic definition that encompasses numbers is highly preferable.
The present article addresses the following problem: what is the best way to define the set Z of integers formally?An ideal definition should be mathematically elegant and compatible with the needs of computer-aided verification.
Our motivation for this question arose in 1996 when applying the LOTOS language [14] to an industrial case study that required signed integers: the predefined library of LOTOS, based on ACT-ONE [9] abstract data types, provided only natural numbers, but no signed integers.At that time, no obvious solution could be found in the literature.During the past decades, various approaches have been devised for this problem and implemented in mainstream specification languages and theorem provers.The present article reviews and compares these approaches.We restrict our study to arbitrary large naturals and integers (i.e., the mathematical sets N and Z), thus excluding finite subranges of N and Z, especially machine numbers (e.g., 32-bit integers) and modular arithmetic.

Definitions of N
In mathematics, natural numbers can be constructed in two main ways: • Construction based on set theory: assuming that the concept of set preexists, we know from the works of Zermelo, Fraenkel, and Von Neumann that natural numbers can be defined as the following sequence of sets: , {∅, {∅}}} and so on, where number n+1 is defined as the set n∪{n}, i.e., {0, 1, ..., n}.We are not aware of any computer language that uses this approach to define natural numbers.Even formal methods based on set theory, such as VDM, Z, B, or TLA+ [18] do not define their naturals this way.
• Construction based on algebraic terms: we know from Peano axioms that natural numbers can be represented as algebraic terms built with two constructors [zero : → N] and [succ : N → N].Because this approach lends itself well to machine execution and automated reasoning, it has been adopted by most computer languages that formally define their numbers.
The simplicity of this approach fits theoretical needs well but faces practical limitations: Peano-style naturals are encoded in base one (unary representation), which is cumbersome when writing large numbers, and inefficient when directly executing algebraic specifications by giving them as input to some rewrite engine or encoding them into some language (e.g., a functional programming language) that supports inductive types and pattern matching; in practice, unary representation often causes stack overflow for naturals larger than 10 6 .
For this reason, refined approaches have been proposed to represent naturals more compactly and reduce the amount of rewrite steps; this is usually done by encoding numbers in a base different from one, e.g., two [2] [4], three [8], four [7], ten [32] [17] [2] [4] [29], sixteen [2], or in some arbitrary base [33,Systems DA and JP].In this article, we stick to the unary representation, since it is used by a majority of specification languages and software tools.
The choice between set theory and algebraic terms for defining unsigned naturals can also be found in the construction of signed integers.In the sequel, we examine each approach in turn.
3 Approach 1: Definition of Z Using Set Theory In mathematical textbooks, the set of integers is defined as Z = (N × N)/ ∼, where × is the cartesian product and ∼ the equivalence relation such that (x, y) ∼ (x , y ) ⇐⇒ x + y = x + y.
This approach has been retained for defining integers in CASL [23,  Having mentioned this approach, we now consider, in the remainder of this article, alternative definitions based upon algebraic terms rather than set theory.
4 Formal definitions

Syntactic notations
Following the established terminology of algebraic specification, we define a sort2 to be a collection of algebraic terms.These terms should be well-typed, meaning all the usual constraints arising from the arity of operations, the sorts of operation arguments, and the sorts of operation results must be taken into account.
If t is a term of sort S, if f is a unary operation [f : S → S], and if n is a natural number, let f n (t) be the term defined inductively by f 0 (t) = def t and f n+1 (t) = def f (f n (t)).For instance, succ 3 (zero) = succ(succ(succ(zero))).
As often as possible, we try to split the set of operations into constructors, which determine the set of possible values of a sort, and non-constructors, which are defined functions specified using algebraic equations or term rewrite rules.We define the constructors of a sort S to be all constructors that return a result of sort S. For instance, the constructors of natural numbers defined in the Peano style are zero and succ; all other operations also returning a natural number (e.g., addition, subtraction, multiplication, etc.) are seen as non-constructors.
We define a ground normal form to be any well-typed term built using constructors only; consequently, a ground normal form cannot contain free vari-ables or non-constructors.We define the domain of a sort S to be the set, noted dom(S), of all ground normal forms of sort S. We define the image of a constructor f to be the set, noted img(f ), of all ground normal forms whose top-level constructor is f .For instance, if nat is the sort defined by the two constructors [zero : → nat] and [succ : nat → nat], its domain is dom(S) = {succ n (zero) | n ∈ N} and the images of its constructors are img(zero) = {zero} and img(succ

Semantic denotations
Our goal is to study how mathematical integers can be conveniently represented by sorts, constructors, and algebraic terms.We thus carefully distinguish between notations (i.e., terms) and denotations (i.e., numbers from Z).
If f is a constructor, we write [[f ]] its intended denotation, which we conveniently formulate as a λ-expression, in which all the trivial conversions between sorts or types (i.e., from Peano-like terms to N, or from N to Z) are implicitly performed.
, meaning that each integer can be denoted by at least one ground normal form of sort S.
Additionally, we say that the constructors of sort S are free if [[.]] is an injection3 from dom(S) to Z, i.e., (∀t , where t 1 = t 2 means the syntactic identity of terms t 1 and t 2 .Thus, if the constructors are free, each integer is denoted by exactly one ground normal form of S. This definition remains compatible with the usual, more general definition stating that constructors are free if any two syntactically different ground normal forms cannot be proven equal (or be rewritten one into the other, or both into a common third term).
Let S be a sort intended to represent Z, and f 1 , ..., f n its constructors (with n ≥ 1).It follows from the above definitions that these constructors are free iff all functions [[f i ]] are injective and the sets of denotations of the images img(f 1 ), , where denotes the disjoint union.Notice that if a sort has a single constructor, this constructor is not necessarily free, as it may be non-injective; various examples will be seen below.
To compare the various definitions of Z based on algebraic terms, we quantify the complexity of each approach using two natural numbers (m, n) defined as follows: given a sort S that represents Z, n is the number of constructors (noted f 1 , ..., f n ) of S, and m is the number of different sorts that occur in the arguments of f 1 , ..., f n ; in particular, m is equal to one if sort S is defined directly, without referencing another sort.The number n of constructors adversely im-pacts the conciseness of non-constructor operations having arguments of sort S: a unary function is likely to require n equations or rewrite rules; a binary function is likely to require n 2 equations or rewrite rules, etc.We write NF m n (resp.F m n ) an approach based on m sorts and n non-free (resp.free) constructors.

Definitions of Z Using Non-Free Constructors
In this section, we review four approaches that quickly come to mind when trying to define integers using algebraic terms.However, these approaches lead to non-free constructors which, we believe, are not optimal.The merit of an approach is inversely proportional to its number of collisions, i.e., the maximal number of distinct ground normal forms that can denote the same integer value.This unique constructor is not free, because of the collision [[pair(plus, zero)]] = [[pair(minus, zero)]] = 0, a situation that we will refer to as the double-zero issue 6 .This is the only collision in this approach.• Dependent types: One may wish to rule out undesirable terms such as pair(minus, zero) or neg(zero).This is the approach followed in the SMT-LIB standard [1, Fig. 3.3 page 34, and page 35], which states: "The set of values for the Int sort consists of all numerals and all terms of the form (−n) where n is a numeral other than 0 ".Prohibiting the algebraically-closed term (−0) can be done trivially, at the level of syntax, but things are far less easy with general terms of the form (−e), where e is an expression containing free variables and/or arbitrary user-defined functions.Deciding whether such terms belong to Int is equivalent to answer the question e = 0, which is undecidable in the general case and would anyway require involved proofs in each particular case.Alternative approaches with efficient decision procedures are thus preferable.

Approach 2: NF
• Equations relating constructors: Other approaches use equations or rewrite rules in order to formalize the relations that may exist between constructors.For instance, [4] and [3] handle the double-zero issue by adding two equations −0 = 0 and − − n = n, which eliminate unwanted terms by bringing them under a suitable normal form.However, this approach weakens the difference between constructors and non-constructors and often raises termination issues.There exists a classical technique (see [27,Sect. 3], [11,Sect. 3.3], etc.) to eliminate non-free constructors by replacing each of them with two distinct operations: a free constructor and a non-constructor.Unfortunately, such a dissociation of roles does not give exploitable results when applied to the construction of Z.
• Subtypes: Another approach relies on subtyping and operator overloading to avoid the double-zero issue.For instance, the predefined type library of Maude [6,  (v) these non-constructors are defined by adding two equations −0 = 0 and − − n = n.This approach is the only one in which values of sort Nat are also values of sort Int; such implicit type conversion closely reflects the mathematical fact that N ⊂ Z, but might become a nuisance when turning an algebraic specification with arbitrary large numbers into a concrete implementation written in a programming language with bounded numbers (e.g., the C language with its int and unsigned int types): to avoid silent, yet unsafe numeric overflows, the programmer will have to detect all implicit type conversions in the formal specification and make them explicit in the program.
These various approaches, even if correct, are heavy.Lighter approaches using only the same basic concepts as for the Peano-style definition of naturals are desirable.All in one, we believe that definitions based on non-free constructors are sub-optimal.Therefore, in the next section, we investigate simpler approaches genuinely based on free constructors.

Definitions of Z Using Free Constructors
Free constructors obviously lead to simpler mathematics.On the practical side too, free constructors are desirable, for at least three reasons: (i) terms defined using free constructors have a unique representation, so that, in principle, no memory bit is wasted due to the existence of multiple representations of the same value; (ii) because each term has a unique representation, comparison of values relies upon syntactic identity, which can be efficiently implemented using bitstring comparison and/or hashing; no extra computation is required to compare multiple representations of the same value or to bring terms under a canonical form first; (iii) increasingly many computer languages (e.g., functional, objectoriented, etc.) are supporting pattern matching with free constructors, whereas the number of tools (e.g., term rewrite engines, tools for algebraic specifications, etc.) that can handle non-free constructors is shrinking, as many tools are no longer maintained7 .

-Three Sorts and Two Free Constructors
The approaches NF 3 1 of Sect.5.3 and NF 2 2 of Sect.5.4 (i.e., defining an integer as the combination of a sign and a natural) can be reused and adapted to a free-constructor setting.This requires to introduce a new sort and to break the symmetry between the negative and positive cases, so as to forbid, by means of statically-decidable type checking, one of the two zero values.
As with Maude, the double-zero issue is avoided by a deliberate dissymmetry between both constructors @cInt and @cNeg, whose arguments have sorts Nat and Pos, respectively.But, contrary to Maude, Nat and Pos are distinct sorts, not subtypes; this requires explicit type conversions and may create confusion for users, who must carefully distinguish between natural and positive values.

Approach 7: F 2 3 -Two Sorts and Three Free Constructors
A different approach can be found in the standard library of the Coq theorem prover8 (see [26] for confirmation).To construct natural numbers, this library defines a sort9 nat built in Peano style using two constructors [0 :→ nat] and [S : nat → nat].In an alternative approach, the Coq library also contains a sort positive that represents N \ {0}, i.e., all natural numbers greater or equal to one -such numbers are encoded in binary form, as unbounded strings of bits inductively defined by three free constructors Notice that this approach could be slightly adapted to define Z using sort N (or nat) rather than positive.In such case, the three constructors would become Contrary to some aforementioned approaches, the Coq library handles negative and positive numbers symmetrically.Even if this approach is modified (as explained in the previous paragraph) to use only two sorts, it still relies upon three constructors, which increases the length of definitions and proofs for most operations on integers; in particular, the usual binary operators are likely to require nine equations (rather than four when only two constructors are used).We have seen so far two definitions of Z based on free constructors: one with three sorts and two constructors (i.e., F 3 2 of Sect.6.1), another one with two sorts and three constructors (i.e., F 2 3 of Sect.6.2).At this point, a natural question is: is there a simpler definition with two sorts and two constructors only?

6.3
The answer is: yes.Such a solution was found in 1996 when trying to extend the standard library of LOTOS with signed integers, and it has been distributed as part of the CADP toolbox since February 1997.This approach uses two sorts Nat, which denotes N, and Int, which denotes Z.The sort Int is built from Nat, without the need for a third sort, using two free constructors Although this approach does not handle negative and positive numbers symmetrically, it enjoys a nice symmetry property, as the denotations of constructors are both involutive functions, i.e., [[pos The constructor pair (pos, neg) is unique in this respect, and there is no simpler solution: consider the set Φ of involutive functions ϕ over Z, i.e., (∀n) (ϕ(ϕ(n)) = n); consider the "simple" elements of Φ, namely affine functions such that (∀n) (ϕ(n) = def an + b), where a and b are constants; the "simple" involutive solutions are either λn.n or all functions of the form λn.(−n+b); among the restrictions to N of these solutions, the only pair of free constructors is pos (which corresponds to a = 1 and b = 0) and neg (which corresponds to a = −1 and b = −1), thus ensuring that It is worth noting that this approach supports straightforward induction on integers, which is lacking in some other approaches -see, e.g., [24,Sect. 8.4 and 8.4.3] for a discussion concerning Isabelle/HOL.Using the pos and neg constructors, induction on integers can be achieved by two inductions on naturals: firstly, one proves that the property P hold for pos(0) and that, if P holds for pos(n), it also holds for pos(n + 1); secondly, one proves that P holds for neg(0) and that, if P holds for neg(n), it also holds for neg(n + 1).

Approach 9: F 2 1 -Two Sorts and One Constructor
Given that Z can be defined as Z = def (N × N)/ ∼ and that there exist bijections from N 2 to N (e.g., diagonal enumeration), it is possible to define the sort int using a single constructor [map : nat → int] that is a bijection from N to Z.There are various choices for map; the simplest definition is likely to be the following one: [[map]] = def λn.(if even(n) then n/2 else − (n + 1)/2), where [even : nat → bool] is the predicate characterizing even natural numbers.This definition gives, as n increases, the following sequence of values for map(n): 0, −1, 1, −2, 2, −3, 3, −4, 4, etc.The elements of Z are thus encoded as follows: n ≥ 0 → map(2n) and n < 0 → map(−2n − 1).This approach F 2 1 is related to the approach F 2 2 of Sect.6.3 by two identities: pos(n) = map(2n) and neg(n) = map(2n + 1).But, even if having a single constructor map is a form of minimality, it does not make the definitions of the usual non-constructors (+, <, etc.) more concise than using the two constructors of approach F 2 2 .Indeed, most definitions still need to distinguish two cases, depending whether some argument is odd or even; approach F 2 1 checks this using conditional equations, whereas approach F 2 2 uses pattern matching.For instance, the incrementation function [incr : int → int] requires two conditional equations: Such a systematic reliance on conditional equations, parity tests, and divisions by two has, at least, three drawbacks: (i) the definitions are neither elegant nor intuitive; (ii) reasoning by induction is not easy; (iii) direct implementation in algebraic or functional languages is not efficient, since parity tests cost O(n) in time (before deciding if a number is odd or even, one needs to visit all its cons subterms).At first sight, this approach looks truly beautiful, as it is the simplest possible extension of the Peano system, to which only one constructor nego of null arity is added.Morever, the definition of sort int is self-contained and does not rely on the existence of a sort nat.A similar approach appears in [8,Sect. 4], where integers are written in base 2 starting from two constants 0 and -1.
However, the definitions of non-constructors can be algorithmically involved with this approach, due to the dual nature of succ (which means either incrementation on positive numbers or decrementation on negative numbers, and might thus complicate induction proofs) and because the sign of a number cannot be immediately determined without traversing all succ constructors until a terminal constant (zero or nego) is reached.Some operations are nevertheless easy to express; this is the case, for instance, of the two predicates [isneg : int → bool] and [ispos : int → bool] that check whether an integer is strictly negative or positive-or-null: But other operations, even the simplest ones, are much less intuitive.For instance, incrementation [incr : int → int] is tricky because it requires either to insert one succ if the argument is positive or to delete one succ if the argument is negative.We believe that this cannot be done without introducing an auxiliary operation [buff : int → int] that keeps one succ in a virtual buffer until the terminal constant gets known, an information that is required to take an insertion-or-deletion decision: To avoid, such intricacies, one can resort to conditional equations, the premises of which use the aforementioned isneg and ispos predicates.This way, incrementation could be defined more concisely: Such equations would be very similar to those of approach F 2 2 of Sect.6.3.Actually, both approaches are related by the two identities pos(succ n (zero)) = succ n (zero) = n and neg(succ n (zero)) = succ n (nego) = −n − 1.The approach F 2  2 is yet simpler: it uses normal equations rather than conditional ones, and determines the sign of a number in time O(1) by pattern matching on the top-level constructor of the term, rather than in time O(n) by invoking the predicates isneg and ispos that visit all subterms to reach an innermost terminal constant.

Conclusion
We have shown that the standard definition of signed integers found in mathematical textbooks (namely, Z = def (N × N)/ ∼) is not well-adapted to the needs of computer science.It has been argued, e.g. in [22, p. 86], that this standard definition, although less straightforward and natural than a term-algebra approach 12 , should nevertheless be preferred because it leads to shorter definitions and proofs by avoiding subdivision into a large number of cases.This argument predates the computer era: it might have been valid when all proofs had to be done manually, but is less relevant today, as interactive or automated theorem provers play an ever-increasing role and handle case disjunctions much better than humans.Moreover, it is unsure that the definitions of arithmetic and relational operators are significantly longer when integers are defined in Peano style with only a few constructors.
In computer science, there is consensus to specify naturals using the Peano constructors zero and succ, but no consensus at all on how integers should be specified.Leaving aside languages that assume the existence of predefined integers, we reviewed ten different ways of defining integers formally: the settheoretical approach, four approaches based on non-free constructors (NF 2  1 , NF 1  3 , NF 3 1 , and NF 2 2 ), and five approaches based on free constructors (F 3 2 , F 2 3 , F 2 2 , F 2 1 , and F 1 3 ).Such a diversity is enjoyable, but too many diverging approaches can be a nuisance.Given the practical usefulness of integers, a common approach would be desirable, so as to ease tool interoperability and enable formal specifications and proofs to be reused.In this respect, the approach F 2 2 of Sect.6.3 would be the best candidate: it is simple, enjoys elegant mathematical properties, allows induction over integers, leads to concise definitions for the usual operations on integers, and has been implemented in the CADP toolbox since 1997.
Finally, considering the alternative approaches that propose more efficient representations for unsigned naturals than the Peano-style unary representation, F 2  2 is orthogonal to some of these approaches, e.g., [2], and could be combined with them to provide efficient representations for signed integers as well.
For instance, [[zero]] = 0 and [[succ]] = λn.(n+ 1).Having defined the denotation [[f ]] of a constructor f , we extend this notion to define the denotation [[t]] of a ground normal form t by induction on the syntax of terms: [

5. 2 5 . 3 1 -
Approach 3: NF 1 3 -One Sort and Three Non-Free Constructors To define a sort Int representing Z, an intuitive idea, used in the library 5 of the KIV tool [10, p. 8 and Sect.5.1 page 42], is to take the Peano system [zero :→ Int] and [succ : Int → Int] and extend it with a third constructor [pred : Int → Int] such that [[pred]] = λn.(n− 1), while zero and succ keep their usual denotations: [[zero]] = 0 and [[succ]] = λn.(n+ 1).Definitions of integers involving such a predecessor operation have been studied in [33, System SP] [29], and also in [4] [3], where the predecessor operation is a nonconstructor.The fact that [[pred]] = [[succ]] −1 gives an appealing symmetry, as pred and succ progress in opposite directions.Unfortunately, these constructors are not free, the simplest counterexample being [[pred(succ(zero))]] = [[succ(pred(zero))]] = 0.Each integer n ≥ 0 can be represented by an infinite number of terms, e.g., {pred k (succ n+k (zero)) | k ∈ N} or, more generally, any combination (in any order) of k applications of pred mixed with (k + n) applications of succ -a dual remark holds if n ≤ 0. The number of collisions is thus ℵ 0 .Approach 4: NF 3 Three Sorts and One Non-Free Constructor Another approach, used in the type library of PSF[30, Sect.8]  [21, Sect.2.5], is based on the idea that an integer is a natural with a sign.Assuming the existence of a sort Sign with two free constructors [plus :→ Sign] and [minus :→ Sign], the sort Int can be defined using a constructor [pair : Sign, Nat → Int] such that [[pair]] = λs.λn.(if s = plus then n else − n).
pages 45-46, 116, and 248-251] makes plain use of order-sorted specifications to define integers as follows: (i) it first defines the sort Nat of naturals, together with the subsort NzNat of Nat, which contains all naturals different from zero; (ii) it then defines the sort Int of integers, together with the subsort NzInt of Int, which contains all integers different from zero and is also a supersort of NzNat; (iii) a "unary-minus" constructor [− : NzNat → NzInt] is defined, such that [[−]] = λn.(−n);(iv) this constructor is extended to integer sorts by introducing two subsort- overloaded non-constructors [− : NzInt → NzInt] and [− : Int → Int];