//
// Course: BU CAS CS 520
// Instructor: Hongwei Xi (hwxi AT cs DOT bu DOT edu)
//

//
// Alonzo Church's encoding of natural numbers in System F
//

(*
datatype Nat = Z of () | S of Nat // Z: Nat and S: Nat -> Nat
*)

typedef Nat = {X:type} (X, X -<cloref> X) -<cloref> X

val Z = lam {X:type} (z: X, s: X -<cloref> X) =<cloref> z
val S = lam (n: Nat) =<cloref>
  lam {X:type} (z: X, s: X -<cloref> X) =<cloref> s(n{X}(z,s))
// end of [val]

(* ****** ****** *)

fn add_Nat_Nat (m: Nat, n: Nat):<> Nat =
  m {Nat} (n, S)

fn mul_Nat_Nat (m: Nat, n: Nat):<> Nat =
  m {Nat} (Z, lam x => add_Nat_Nat (n, x))

val One = S (Z) : Nat

// m^n
fn pow_Nat_Nat (m: Nat, n: Nat):<> Nat =
  n {Nat} (One, lam x => mul_Nat_Nat (m, x))
  
(* ****** ****** *)

typedef int = intptr
val _0 = intptr_of_int (0)
val _1 = intptr_of_int (1)

fn print_Nat (n: Nat): void = let
  val _n = n{int} (_0, lam x => x + _1)
in
  print_intptr (_n)
end // end of [print_Nat]

(* ****** ****** *)

val Two = S (One) : Nat
val () = (print "Two = "; print_Nat Two; print_newline ())

val Three = add_Nat_Nat (One, Two)
val () = (print "Three = "; print_Nat Three; print_newline ())

val Six = mul_Nat_Nat (Two, Three)
val () = (print "Six = "; print_Nat Six; print_newline ())

val Eight = pow_Nat_Nat (Two, Three)
val () = (print "Eight = "; print_Nat Eight; print_newline ())

(* ****** ****** *)

implement main () = ()

(* ****** ****** *)

(* end of [numeral.dats] *)