2.2. Natural Numbers

2.2.1. Basic Definitions

type Nat :=
    zero: Nat
    succ: Nat -> Nat

one: Nat := succ zero

(+): Nat -> Nat -> Nat := case
    \ zero, b     := b
    \ succ n, b   := succ (n + b)

(*): Nat -> Nat -> Nat := case
    \ zero, _     := zero
    \ succ n, b   := b + n * b

(^): Nat -> Nat -> Nat := case
    \ a,  zero    := one
    \ a,  succ n  := a * a^n


type (<=): Nat -> Nat -> Prop :=
    start {n}: zero <= n
    next  {n m}: n <= m  ->  succ n <= succ m

(<) (a b: Nat): Prop :=
    succ a <= b

2.2.2. Zero is Different from a Successor

zeroNeSucc: all {n: Nat}: zero = succ n -> False :=
    case        -- no clauses

A potential clause would have the form

case
\ {n}, same {?m} := ?
--      : ?m = ?m

This would require to unify zero = succ n with ?m = ?m which is impossible, because ?m cannot be zero and succ n at the same time (zero and succ n are not unifiable).

2.2.3. Properties of Addition

Having a + succ b it is possible to pull out the successor function getting succ (a + b). This can be proved by induction on a. The base case is proved by reflexivity. The induction step requires to prove the equality of the two expressions

-- expressions                          normal forms
succ n + succ b                         succ (n + succ b)
succ (succ n + b)                       succ (succ (n + b))

This can be done by the induction hypothesis using congruence.

pullSucc: all {a b: Nat}: a + succ b = succ (a + b) := case
    \ {zero},   {_} := refl
    \ {succ n}, {_} := congruence {succ} pullSucc

Zero is the neutral element of addition. The left neutrality doesn’t need a proof. It is evident by normalisation. The right neutrality a + zero = a requires an induction proof.

zeroRightNeutral: all {a: Nat}: a + zero = a := case
    \ {zero}   := refl
    \ {succ n} := congruence {succ} zeroRightNeutral

Addition is commutative i.e. a + b = b + a. We prove this by induction on a. The base case is proved by right neutrality of zero. The induction case requires a proof of the equality of the two left expressions

-- expression                       normal form
succ n + b                          succ (n + b)
b + succ n                          b + succ n

Via the induction hypothesis and congruence we can prove the equality of succ (n + b) and succ (b + n). The flipped version of pullSucc can transform it into b + succ n.

plusCommutes: all {a b: Nat}: a + b = b + a := case
    \ {zero},   {b}   := zeroRightNeutral
    \ {succ n}, {b}   := (congruence {succ} plusCommutes, flip pullSucc)

The associativity of addition

(a + b) + c = a + (b + c)

can be proved by induction on a. The base case is trivial. The proof of the induction step can be done by the equivalences

(succ n + b) + c
=                           -- normalisation
succ ((n + b) + c)
=                           -- induction hypothesis + congruence
succ (n + (b + c))
=                           -- normalisation (bwd)
succ n + (b + c)
plusAssociates: all {a b c: Nat}: (a + b) + c = a + (b + c)
:= case
    \ {zero},   {b}, {c} := same
    \ {succ n}, {b}, {c} := congruence {succ} plusAssociates

2.2.4. Properties of Multiplication

Multiplication distributes over addition. In order to prove this we need a helper theorem.

plusSwap: all {a b c: Nat}: a + (b + c) = b + (a + c)
:=
    ( flip plusAssociates:                    _ = (a + b) + c
    , congruence {\ x := x + c} plusCommutes: _ = (b + a) + c
    , plusAssociates:                         _ = b + (a + c)
    )

Having plusSwap we can prove the distributivity of multiplication

a * (b + c) = a * b + a * c

by induction on a. The base case is trivial. The induction step requires a proof of

succ n * (b + c) = succ n * b + succ n * c

The equality can be proved by the steps

succ n * (b + c)
=                               -- normalisation
(b + c) + n * (b + c)
=                               -- associativity of addition
b + (c + n * (b + c))
=                               -- induction hypothesis + congruence
b + (c + (n * b + n * c))
=                               -- plusSwap + congruence
b + (n * b + (c + n * c))
=                               -- associativity of addition (bwd)
(b + n * b) + (c + b * c)
=                               -- normalization (bwd)
succ n * b + succ n * c
timesDistributes: all {a b c: Nat}: a * (b + c)  =  a * b + a * c
    -- Multiplication distributes over addition
:= case
    \ {zero},    {b},   {c} := refl
    \ {succ n},  {b},   {c} :=
        -- goal:
        --       succ n * (b + c)
        --       =
        --       succ n * b + succ n * c
        ( plusAssociates:
                succ n * (b + c)
                =
                b + (c + n * (b + c))

        , congruence
                {\ x := b + (c + x)}
                timesDistributes
            :   _
                =
                b + (c + (n * b + n * c))

        , congruence
            {\ x :=  b + x}
            plusSwap
            :  _
               =
               b + (n * b + (c + n * c))

        , flip plusAssociates
            :  _
               =
               succ n * b + succ n * c
               -- (b + n * b) + (c + n * c)
        )

2.2.5. Properties of Order

Reflexivity

leReflexive: all {a: Nat}: a <= a
    -- The less equal relation is reflexive.
:= case
    \ {zero}      :=  start
    \ {succ _}    :=  next leReflexive

-- with implicits made explicit
    \ {zero}      := start {zero}
    \ {succ n}    := next {n} {n} (leReflexive {n})

Inversion

leInvers {a b: Nat}: succ a <= succ b  ->  a <= b
    -- If two successors are less equal then the values are
    -- less equal as well.
:= case
    -- constructor 'start' not possible, its type is 'zero <= .'
    \ next le := le

    -- with implicits
    \ next {a} {b} le := le

Transitivity

(,): all {a b c: Nat}: a <= b -> b <= c -> a <= c
    -- The '<=' relation is transitive
:= case
    \ start,        _           := start
    \ next leAB,    next leBC   := next (leAB, leBC)

    -- with implicits
    \ {zero}, {b}, {c}, start {b}, _ :=

        start {c}

    \ {succ a}, {succ b}, {succ c}, next {a} {b} leAB, next {b} {c} leBC :=

        next {a} {c} ((,) {a} {b} {c} leAB leBC)

Others

ltIrreflexive: all {a: Nat}: a < a -> False
    -- The less than relation is irreflexive.
:= case
    -- The 'start' constructor constructs 'zero <= _' which cannot be
    -- unified with 'succ ?a <= ?a'.
    \ next lt := ltIrreflexive lt


leLtOrEq: all {a b: Nat}: a <= b -> a < b \/ a = b
:= case
    \ {zero},   {zero},     start   := right refl
    \ {zero},   {succ _},   start   := left (next start)
    \ {succ n}, {succ m},   next le :=
        match
            leLtOrEq le: n < m \/ n = m
        case
            \ left  lt  := left  (next lt)
            \ right eq  := right (congruence {succ} eq)


leSucc: all {a: Nat}: a <= succ a
    -- All numbers are less or equal their successors
:= case
    \ {zero}      := start
    \ {succ n}    := next leSucc



zeroLeast: all {a: Nat}: a <= zero  ->  a = zero
    -- All numbers less or equal 'zero' are 'zero'
:= case
    \ start := same
    -- The case 'next' is not possible!


notLtZero: all {a: Nat}: a < zero -> False
    -- No number is less than 'zero'
:= case
    -- neither start nor next can construct an object of
    -- type 'succ a <= zero'


ltSucc {a: Nat}: a < succ a
    -- All numbers are less than their successors
:=
    leReflexive

2.2.6. Order and Predicates

LowerBound (P: Nat -> Prop) (x: Nat): Prop
        -- 'x' is a lower bound for all numbers satisfying 'P'
:=
    all {y}: P y  ->  x <= y


StrictLowerBound (P: Nat -> Prop) (x: Nat): Prop
        -- 'x' is a strict lower bound for all numbers satisfying 'P'
:=
    all {y}: P y  ->  x < y


Least (P: Nat -> Prop) (x: Nat): Prop
    -- 'x' is the smallest number satisfying 'P'
:=
    LowerBound P x /\ P x


lowerBoundSucc
    {n: Nat} (lbN: LowerBound P n) (notPN: Not P n)
    : LowerBound P (succ n)
:=
    \ {y} (pY: P y): succ n <= y :=
        match leLtOrEq (lbN pY) case
            \ left  lt  := lt
            \ right eq  := notPN (replace {P} (flip eq) pY)

2.2.7. Difference

(-): all (a b: Nat) {le: b <= a}: Nat
:= case
    \ (a := zero)    zero       start     := a
    \ (a := succ _)  zero       start     := a
    \ (succ n)       (succ m)   (next le) := n - m

   -- or better
   case
     a        zero     {start}    := a
     (succ n) (succ m) {next le}  := (n - m) {le}

Note that the pattern match on b <= a is allowed in the case clauses, because only one constructor is possible. Therefore no decision is made on the propositional pattern match.

minusPlusInvers: all {a b: Nat}: b <= a -> a - b + b = a
:= case
    \ {zero},       {zero},     _       := zeroRightNeutral
    \ {succ n},     {zero},     _       := zeroRightNeutral
    \ {succ n},     {succ m},   next le :=
        -- goal: (succ n - succ m) + succ m = succ n
        -- i.e.: (n - m) + succ m = succ n
        (
            pullSucc: _ = succ ((n - m) + m)
        ,
            mapEquals (minusPlusInvers le)
        )
minusLe: all {a b: Nat}: b <= a -> a - b <= a
    -- Substraction makes a number less equal.
:= case
    \ {zero},   {zero},   start     := start
    \ {succ n}, {zero},   start     := leReflexive
    \ {succ n}, {succ m}, next le   :=
        -- goal: succ n - succ m <= succ n
        -- i.e.: n - m  <= succ n
        (
            minusLe le: n - m <= n
        ,
            leSucc:     n <= succ n
        )

From a < b we can infer c - b < c - a provided that b <= c is valid.

minusLt: all {a b c: Nat}: a <= c  ->  b <= c  ->  a < b  ->  c - b < c - a
    -- The preconditions 'a <= c'  and 'b <= c' are needed for '-'
:= case
    \ start,        next leBC,      next ltAB   :=
        -- goal: succ c - succ b < succ c - zero
        -- i.e.: succ (c - b) <= succ c
        next (minusLe leBC)
    \ next leAC,    next leBC,      next ltAB   :=
        -- goal: succ c - succ b < succ c - succ a
        -- i.e.: c - b < c - a
        minusLt leAC leBC ltAB

2.2.8. Wellfounded Recursion

Clearly all natural numbers are finite, because each number is constructed by finitely many applications of the successor function. But here we invent another way to express the finiteness of natural numbers.

We say that a number is finite, if all numbers below it are finite.

type Finite: Nat -> Prop :=
    fin {x}: (all {y}: y < x -> Finite y) -> Finite x

We can prove that all natural numbers are finite by an induction proof.

natFinite: all {n: Nat}: Finite n
:= case
    \ {zero} :=
        fin notLtZero

    \ {succ n} :=
        -- goal: Finite (succ n)
        let
            aux: all {n}: Finite n -> all {y}: y < succ n -> Finite y
            := case
                -- no match for
                --      {_} _ {_} start
                -- because
                --      succ y <= x
                -- cannot be unified with
                --      zero <= x

                {n} (finN := fin f) {y} (next le) :=
                    match
                        leLtOrEq (le: y <= n): y < n \/ y = n
                    case
                        left  lt  :=
                            (f: all {y}: y < n -> Finite y)
                                lt

                        right eq  :=
                            cast (flip eq) finN
        :=
            fin (aux natFinite: all {y}: y < succ n -> Finite y)
            : Finite (succ n)

Unbounded search:

find {P: Nat -> Prop} (d: Decider P): Exist P -> Refine (Least P)
:= case
    \ (w, pW) :=
        let
            aux: all n:
                    n <= w  ->              -- invariant 1
                    LowerBound P n ->       -- invariant 2
                    Finite (w - n) ->       -- bound function
                    Decision P n   ->
                    Refine (Least P)
            := case
                n _ lbN _ (true pN) :=
                    (n, lbN, pN)

                n leNW lbN (fin f) (false notPN) :=
                    let
                        lbSN: LowerBound P (succ n) :=
                            lowerBoundSucc lbN notPN

                        leSNW: succ n <= w :=
                            lbSN pW

                        ltWmSN: w - succ n < w - n :=
                            minusLt leNW leSNW leReflexive
                    :=
                        aux (succ n) leSNW lbSN (f ltWnSN) (d (succ n))
        :=
            aux zero start (\ _ := start) natFinite (d zero)