//
//
// This file is for Assignment 4, BU CAS CS 520, Fall, 2008
//
//

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

datasort num = // abstract
dataprop EQUAL (num, num) = {x:num} EQUAL (x, x)
  
abst@ype N (num)

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

absprop ONE (num)

extern val one: [x:num] (ONE x | N x)

extern praxi one_isfun {x1,x2:num}
  (pf1: ONE x1, pf2: ONE x2):<prf> EQUAL (x1, x2)

extern praxi one_istot (): [x:num] ONE x

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

absprop MUL (num, num, num)

extern praxi mul_isfun {x1,x2:num} {x,x':num}
  (pf1: MUL (x1, x2, x), pf2: MUL (x1, x2, x')):<prf> EQUAL (x, x')

extern praxi mul_istot {x1,x2:num} ():<> [x:num] MUL (x1, x2, x)

extern praxi mul_one_unit_l {x1,x2,x3:num}
  (pf1: ONE x1, pf2: MUL (x1, x2, x3)):<prf> EQUAL (x2, x3)

extern praxi mul_one_unit_r {x1,x2,x3:num}
  (pf1: ONE x1, pf2: MUL (x2, x1, x3)):<prf> EQUAL (x2, x3)

extern praxi mul_assoc {x1,x2,x3,x12,x23,x123:num}
  (pf12: MUL (x1, x2, x12), pf23: MUL (x2, x3, x23), pf123: MUL (x12, x3, x123))
  :<prf> MUL (x1, x23, x123)

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

extern fun mul_num_num {x1,x2:num}
  (_: N x1, _: N x2):<> [x:num] (MUL (x1, x2, x) | N x)

overload * with mul_num_num

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

dataprop POWER (num, int, num) =
  | {x:num} {r:num} POWERbas (x, 0, r) of ONE r
  | {x:num} {n:nat} {r,r1:num}
    POWERind (x, n+1, r1) of (POWER (x, n, r), MUL (x, r, r1))

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

prfun power_isfun {x:num} {n:nat} {r1,r2:num} .<n>.
  (pf1: POWER (x, n, r1), pf2: POWER (x, n, r2)): EQUAL (r1, r2) =
  case+ (pf1, pf2) of
  | (POWERbas pf10, POWERbas pf20) => one_isfun (pf10, pf20)
  | (POWERind (pf11, pf12), POWERind (pf21, pf22)) => let
      prval EQUAL () = power_isfun (pf11, pf21)
      prval EQUAL () = mul_isfun (pf12, pf22)
    in
      EQUAL ()
    end
// end of [power_isfun]

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

// please implement the following function
// -- 30 points for a correct implementation
// -- additional 50 points for a correct implementation that is O(log n)
// -- additional 70 points for a correct implementation that is tail-recursive and O(log n)
extern fun power {x:num} {n:nat}
  (x: N x, n: int n):<> [r:num] (POWER (x, n, r) | N r)

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

(* end of [power.dats] *)