app load ["bossLib", "QLib", "numLib"];

open bossLib arithmeticTheory numLib;
infix 8 by;


(*---------------------------------------------------------------------------
     An efficient divide-and-conquer-style exponentiation function.
 ---------------------------------------------------------------------------*)

val exp_def = 
 Hol_defn "exp"
     `(exp x 0       = 1) 
  /\  (exp x (SUC 0) = x)
  /\  (exp x n       = if n MOD 2 = 0 
                        then let v = exp x (n DIV 2) in v * v
                         else  x * exp x (n-1))`;


(*---------------------------------------------------------------------------
        Remove termination constraints.
 ---------------------------------------------------------------------------*)

val (exp_eqns, exp_ind) = Defn.tprove 
(exp_def,
 WF_REL_TAC exp_def `measure SND` THENL
   [RW_TAC arith_ss [], 
    RW_TAC std_ss []
      THEN MP_TAC (Q.SPEC `SUC(SUC v4)` 
                  (REWRITE_RULE [DECIDE `0<2`] (Q.SPEC`2` DIVISION)))
      THEN RW_TAC arith_ss []]);


(*---------------------------------------------------------------------------*
 * A form of correctness: exp is equal to the prim. rec defn. of EXP         *
 * given by:                                                                 *
 *                                                                           *
 *     m EXP 0       = 1                                                     *
 *     m EXP (SUC n) = m * m EXP n                                           *
 *                                                                           *
 *---------------------------------------------------------------------------*)

val EXP_eq_exp = Q.prove
(`!x m. x EXP m = exp x m`,
 recInduct exp_ind THEN REPEAT CONJ_TAC 
   THEN RW_TAC arith_ss [EXP, exp_eqns]
   THEN POP_ASSUM (SUBST1_TAC o SYM)
   THEN RW_TAC arith_ss [GSYM EXP_ADD] 
   THEN `2 * (SUC(SUC v4) DIV 2) = SUC(SUC v4)` 
        by PROVE_TAC [DIVISION,DECIDE `0<2`,ADD_0,MULT_SYM]
   THEN PROVE_TAC [EXP]);


(*---------------------------------------------------------------------------
      Some computations with exp. We first have to come up with
      a version of "exp" where "SUC" constructors are not on the 
      lhs of the recursion equation.
 ---------------------------------------------------------------------------*)

val alt_exp_eqns = Q.prove(
  `exp x n = 
      if n=0 then 1 else 
      if n=1 then x else
      if n MOD 2 = 0 
         then let v = exp x (n DIV 2) in v * v
         else  x * exp x (n-1)`,
RW_TAC std_ss [] 
  THENL [RW_TAC arith_ss [exp_eqns],
         RW_TAC arith_ss [ONE,exp_eqns],
         ALL_TAC, ALL_TAC]
  THEN `?k. n = SUC(SUC k)` 
         by 
        (Q.EXISTS_TAC `PRE(PRE n)`
            THEN REPEAT (Q.PAT_ASSUM `~P` MP_TAC) THEN ARITH_TAC)
  THEN RW_TAC std_ss []
  THEN RW_TAC std_ss [exp_eqns]);


local open computeLib
      val compset = bossLib.initial_rws()
      val _ = add_thms [alt_exp_eqns] compset
in
val Eval = Count.apply (CBV_CONV compset) o Term
end;

Eval `exp 2 1`;
Eval `exp 2 4 * exp 4 2`;
Eval `exp 2 10`;
Eval `exp 2 16`;
Eval `exp 2 100`;

(*---------------------------------------------------------------------------
   REDUCE_CONV is faster than Eval for exp because of 
   numeral representation. 
 ---------------------------------------------------------------------------*)

Count.apply REDUCE_CONV (Term `2 EXP 100`);
