union Option<T> {
  none
  just(T)
}

fun after<T, U>(o:Option<T>, f:(fn T -> U)) {
  switch o {
    case none {
      @none<U>
    }
    case just(x) {
      just(f(x))
    }
  }
}

fun default<T>(o:Option<T>, y:T) {
  switch o {
    case none {
      y
    }
    case just(x) {
      x
    }
  }
}

or_not: (all P:bool, Q:bool. (if ((P or Q) and not P) then Q))

ex_mid: (all b:bool. (b or not b))

or_sym: (all P:bool, Q:bool. (P or Q) = (Q or P))

and_sym: (all P:bool, Q:bool. (P and Q) = (Q and P))

eq_true: (all P:bool. (P  (P = true)))

eq_false: (all P:bool. (not P  (P = false)))

iff_equal: (all P:bool, Q:bool. (if (P  Q) then P = Q))

iff_symm: (all P:bool, Q:bool. (if (P  Q) then (Q  P)))

iff_trans: (all P:bool, Q:bool, R:bool. (if ((P  Q) and (Q  R)) then (P  R)))

contrapositive: (all P:bool, Q:bool. (if ((if P then Q) and not Q) then not P))

double_neg: (all P:bool. not not P = P)

fun xor(x:bool, y:bool) {
  if x then
    not y
  else
    y
}

xor_true: (all b:bool. xor(true, b) = not b)

xor_false: (all b:bool. xor(false, b) = b)

xor_commute: (all a:bool, b:bool. xor(a, b) = xor(b, a))

union Nat {
  zero
  suc(Nat)
}

recursive operator +(Nat,Nat) -> Nat{
  operator +(zero, m) = m
  operator +(suc(n), m) = suc(n + m)
}

recursive operator *(Nat,Nat) -> Nat{
  operator *(zero, m) = zero
  operator *(suc(n), m) = m + n * m
}

recursive expt(Nat,Nat) -> Nat{
  expt(zero, n) = suc(zero)
  expt(suc(p), n) = n * expt(p, n)
}

fun operator ^(a:Nat, b:Nat) {
  expt(b, a)
}

recursive max(Nat,Nat) -> Nat{
  max(zero, n) = n
  max(suc(m'), n) = 
    switch n {
      case zero {
        suc(m')
      }
      case suc(n') {
        suc(max(m', n'))
      }
    }
}

recursive min(Nat,Nat) -> Nat{
  min(zero, n) = zero
  min(suc(m'), n) = 
    switch n {
      case zero {
        zero
      }
      case suc(n') {
        suc(min(m', n'))
      }
    }
}

fun pred(n:Nat) {
  switch n {
    case zero {
      zero
    }
    case suc(n') {
      n'
    }
  }
}

recursive operator ∸(Nat,Nat) -> Nat{
  operator ∸(zero, m) = zero
  operator ∸(suc(n), m) = 
    switch m {
      case zero {
        suc(n)
      }
      case suc(m') {
        n  m'
      }
    }
}

recursive operator ≤(Nat,Nat) -> bool{
  operator ≤(zero, m) = true
  operator ≤(suc(n'), m) = 
    switch m {
      case zero {
        false
      }
      case suc(m') {
        n'  m'
      }
    }
}

fun operator <(x:Nat, y:Nat) {
  suc(x)  y
}

fun operator >(x:Nat, y:Nat) {
  y < x
}

fun operator ≥(x:Nat, y:Nat) {
  y  x
}

recursive summation(Nat,Nat,(fn Nat -> Nat)) -> Nat{
  summation(zero, begin, f) = zero
  summation(suc(k), begin, f) = f(begin) + summation(k, suc(begin), f)
}

recursive iterate<T>(Nat,T,(fn T -> T)) -> T{
  iterate(zero, init, f) = init
  iterate(suc(n), init, f) = f(@iterate<T>(n, init, f))
}

recursive pow2(Nat) -> Nat{
  pow2(zero) = suc(zero)
  pow2(suc(n')) = suc(suc(zero)) * pow2(n')
}

recursive equal(Nat,Nat) -> bool{
  equal(zero, n) = 
    switch n {
      case zero {
        true
      }
      case suc(n') {
        false
      }
    }
  equal(suc(m'), n) = 
    switch n {
      case zero {
        false
      }
      case suc(n') {
        equal(m', n')
      }
    }
}

recursive dist(Nat,Nat) -> Nat{
  dist(zero, n) = n
  dist(suc(m), n) = 
    switch n {
      case zero {
        suc(m)
      }
      case suc(n') {
        dist(m, n')
      }
    }
}

fun log(n) {
  find_log(n, zero, zero)
}

fun lit(a:Nat) {
  a
}

lit_idem: (all x:Nat. lit(lit(x)) = lit(x))

auto lit_idem

suc_lit: (all n:Nat. suc(lit(n)) = lit(suc(n)))

auto suc_lit

auto zero_add

nat_zero_add: (all y:Nat. ℕ0 + y = y)

auto nat_zero_add

auto add_zero

nat_add_zero: (all x:Nat. x + ℕ0 = x)

auto nat_add_zero

lit_suc_add: (all x:Nat, y:Nat. lit(suc(x)) + lit(y) = lit(suc(lit(x) + lit(y))))

auto lit_suc_add

add_commute: (all n:Nat. (all m:Nat. n + m = m + n))

add_assoc: (all m:Nat, n:Nat, o:Nat. (m + n) + o = m + (n + o))

assoc_add: (all m:Nat, n:Nat, o:Nat. m + (n + o) = (m + n) + o)

add_both_sides_of_equal: (all x:Nat. (all y:Nat, z:Nat. ((x + y = x + z)  (y = z))))

left_cancel: (all x:Nat. (all y:Nat, z:Nat. (if x + y = x + z then y = z)))

add_both_monus: (all z:Nat, y:Nat, x:Nat. (z + y)  (z + x) = y  x)

add_monus_identity: (all m:Nat. (all n:Nat. (m + n)  m = n))

monus_monus_eq_monus_add: (all x:Nat. (all y:Nat. (all z:Nat. (x  y)  z = x  (y + z))))

monus_order: (all x:Nat, y:Nat, z:Nat. (x  y)  z = (x  z)  y)