app load ["Mosml", "U"];

quotation := true;

(* Start session *)

val sock = Mosml.time U.createServer ("./cpe", 30);   (* Takes awhile *)
fun ask (q :unit frag list) = 
    case (U.sendRqt sock q)
     of U.None s => U.stdOutput (String.concat["Failed: \n", s, "\n"])
      | U.Some s => U.stdOutput (String.concat[s, "\n"]);

(*---------------------------------------------------------------------------*
 *          Examples                                                         *
 *---------------------------------------------------------------------------*)

(* Make some basic requests *)
ask `Arith !x y z. x < y /\ y < z ==> x < z`;
ask `FOL ((?x. P x) ==> Q) = (!x. P x ==> Q)`;
ask `FOL x /\ y = y + x`;  (* Parsing Error *)
ask `FOL x = x`; (* 12 *)
ask `FOL x + y = y + x`;   (* Can't prove without commutativity *) (* 20 *)
ask `ConcType btree = Leaf of 'a
                    | Brh of 'a => btree => btree`;   (* Succeeds *)
ask `ConcType ctree = Lf of 'a
                    | Bh of (ctree -> 'a) => ctree`;  (* Fails *)
ask `Taut x/\y\/z = (x\/z) /\ (y \/ z)`;
ask `Taut x/\y\/z = (x/\z) /\ (y \/ z)`;
ask `Define fact x = (x=0 => 1 | x * fact(x-1))`;  (* Currently wrong *)
ask `Define (Fact 0 = 1) /\ (Fact (SUC n) = SUC n * Fact n)`;
ask ``;
ask `teddy`; 

(*---------------------------------------------------------------------------*
 * Exercising goal stack interface.                                          *
 *---------------------------------------------------------------------------*)

ask `Goal !x y z. x < y /\ y < z ==> x < z`;
ask `ApplyTac Arith`;  (* Fails : wrong name *)
ask `Drop`; 
ask `TopGoals`;  (* Fails, no goalstacks. *)

ask `Goal (!x y z. P x y /\ P y z ==> P x z) /\
   (!x y z. Q x y /\ Q y z ==> Q x z) /\
   (!x y. P x y ==> P y x) /\
   (!x y. P x y \/ Q x y)
   ==> (!x y. P x y) \/ (!x y. Q x y)`;

ask `ApplyTac PROVE_TAC`;

ask `Goal (!X. equal(X,X)) /\
 (!Y X. equal(X,Y) ==> equal(Y,X)) /\
 (!Y X Z. equal(X,Y) /\ equal(Y,Z) ==> equal(X,Z)) /\
 (!Y X. equivalent(X,Y) ==> there_exists(X)) /\
 (!X Y. equivalent(X,Y) ==> equal(X,Y)) /\
 (!X Y. there_exists(X) /\ equal(X,Y) ==> equivalent(X,Y)) /\
 (!X. there_exists(domain(X)) ==> there_exists(X)) /\
 (!X. there_exists(codomain(X)) ==> there_exists(X)) /\
 (!Y X. there_exists(compose(X,Y)) ==> there_exists(domain(X))) /\
 (!X Y. there_exists(compose(X,Y)) ==> equal(domain(X),codomain(Y))) /\
 (!X Y. there_exists(domain(X)) /\ equal(domain(X),codomain(Y)) ==> 
        there_exists(compose(X,Y))) /\
 (!X Y Z. equal(compose(X,compose(Y,Z)),compose(compose(X,Y),Z))) /\
 (!X. equal(compose(X,domain(X)),X)) /\
 (!X. equal(compose(codomain(X),X),X)) /\
 (!X Y. equivalent(X,Y) ==> there_exists(Y)) /\
 (!X Y. there_exists X /\ there_exists Y /\ equal(X,Y) ==> equivalent(X,Y)) /\
 (!Y X. there_exists(compose(X,Y)) ==> there_exists(codomain(X))) /\
 (!X Y. there_exists(f1(X,Y)) \/ equal(X,Y)) /\
 (!X Y. equal(X,f1(X,Y)) \/ equal(Y,f1(X,Y)) \/ equal(X,Y)) /\
 (!X Y. equal(X,f1(X,Y)) /\ equal(Y,f1(X,Y)) ==> equal(X,Y)) /\
 (!X Y. equal(X,Y) /\ there_exists(X) ==> there_exists(Y)) /\
 (!X Y Z. equal(X,Y) /\ equivalent(X,Z) ==> equivalent(Y,Z)) /\
 (!X Z Y. equal(X,Y) /\ equivalent(Z,X) ==> equivalent(Z,Y)) /\
 (!X Y. equal(X,Y) ==> equal(domain(X),domain(Y))) /\
 (!X Y. equal(X,Y) ==> equal(codomain(X),codomain(Y))) /\
 (!X Y Z. equal(X,Y) ==> equal(compose(X,Z),compose(Y,Z))) /\
 (!X Z Y. equal(X,Y) ==> equal(compose(Z,X),compose(Z,Y))) /\
 (!A B C. equal(A,B) ==> equal(f1(A,C),f1(B,C))) /\
 (!D F' E. equal(D,E) ==> equal(f1(F',D),f1(F',E))) /\
 (there_exists(compose(a,b))) /\
 (!Y X Z. equal(compose(compose(a,b),X),Y) /\
          equal(compose(compose(a,b),Z),Y) ==> equal(X,Z)) /\
 (there_exists(compose(b,h))) /\
 (equal(compose(b,h),compose(b,g))) /\
 (~equal(h,g)) 
  ==> 
     F`;

ask `ApplyTac PROVE_TAC`;
ask `Restart`;

(*---------------------------------------------------------------------------*
 * Euclid's theorem.                                                         *
 *---------------------------------------------------------------------------*)
ask `Define divides a b = ?x. b = a * x`;
ask `Fixity divides Infix 600`;
ask `Define prime p = ~(p=1) /\ !x. x divides p ==> (x=1) \/ (x=p)`;

ask `Goal !x. x divides 0`;
ask `ApplyTac PROVE_TAC divides_def MULT_CLAUSES`;
ask `StoreTop DIVIDES_0`;
ask `Drop`;

ask `Goal !x. 0 divides x = (x = 0)`;
ask `ApplyTac PROVE_TAC divides_def MULT_CLAUSES`;
ask `StoreTop DIVIDES_ZERO`;

ask `Goal !x. x divides 1 = (x = 1)`;
ask `ApplyTac PROVE_TAC divides_def MULT_CLAUSES MULT_EQ_1`;
ask `StoreTop DIVIDES_ONE`;

ask `Goal !x. x divides x`;
ask `ApplyTac PROVE_TAC divides_def MULT_CLAUSES`;
ask `StoreTop DIVIDES_REFL`;

ask `Goal !a b c. a divides b /\ b divides c ==> a divides c`;
ask `ApplyTac PROVE_TAC divides_def MULT_ASSOC`;
ask `StoreTop DIVIDES_TRANS`;

ask `Goal !d a b. d divides a /\ d divides b ==> d divides (a + b)`;
ask `ApplyTac PROVE_TAC divides_def LEFT_ADD_DISTRIB`;
ask `StoreTop DIVIDES_ADD`;

ask `Goal !d a b. d divides a /\ d divides b ==> d divides (a - b)`;
ask `ApplyTac PROVE_TAC divides_def LEFT_SUB_DISTRIB`;
ask `StoreTop DIVIDES_SUB`;

ask `Goal !d a b. d divides a /\ d divides (a + b) ==> d divides b`;
ask `ApplyTac PROVE_TAC ADD_SUB ADD_SYM DIVIDES_SUB`;
ask `StoreTop DIVIDES_ADDL`;

ask `Goal !d a x. d divides a ==> d divides (x * a)`;
ask `ApplyTac PROVE_TAC divides_def MULT_ASSOC MULT_SYM`;
ask `StoreTop DIVIDES_LMUL`;

ask `Goal !d a x. d divides a ==> d divides (a * x)`;
ask `ApplyTac PROVE_TAC MULT_SYM DIVIDES_LMUL`;
ask `StoreTop DIVIDES_RMUL`;

ask `Goal !m n. m divides n ==> m <= n \/ (n = 0)`;
ask `ApplyTac RW_TAC arith_ss divides_def`;
ask `ApplyTac Cases_on x`;
ask `ApplyTac RW_TAC arith_ss MULT_CLAUSES`;  (* should say goal was proved *)
ask `ApplyTac RW_TAC arith_ss MULT_CLAUSES`;
ask `StoreTop DIVIDES_LE`;

ask `Goal !m n. 0 < m /\ m <= n ==> m divides (FACT n)`;
ask `ApplyTac Induct_on n - m`;
ask `ApplyTac RW_TAC bool_ss`;
ask `ApplyTac Have "m:num = n" DECIDE_TAC`;
ask `ApplyTac Have "?k. m = SUC k"
     PROVE_TAC num_CASES LESS_REFL`;
ask `ApplyTac PROVE_TAC FACT DIVIDES_RMUL DIVIDES_REFL`;
ask `ApplyTac RW_TAC bool_ss`;
ask `ApplyTac Have "0 < n" DECIDE_TAC`;
ask `ApplyTac Have "?k. n = SUC k"
      PROVE_TAC num_CASES LESS_REFL`;
ask `ApplyTac RW_TAC arith_ss FACT DIVIDES_LMUL`;
ask `StoreTop DIVIDES_FACT`;

ask `Goal ~prime 0`;
ask `ApplyTac RW_TAC arith_ss prime_def DIVIDES_0`;
ask `StoreTop NOT_PRIME_0`;

ask `Goal ~prime 1`;
ask `ApplyTac RW_TAC arith_ss prime_def`;
ask `StoreTop NOT_PRIME_1`;

ask `Goal ~(2=1) /\ ~(2=0) /\ (x <= 2 = (x=0) \/ (x=1) \/ (x=2))`;
ask `ApplyTac DECIDE_TAC`;
ask `StoreTop TWOfacts`;

ask `Goal prime 2`;
ask `ApplyTac RW_TAC arith_ss prime_def`;
ask `ApplyTac PROVE_TAC DIVIDES_LE DIVIDES_ZERO TWOfacts`;
ask `StoreTop PRIME_2`;

ask `Goal !p. prime p ==> 0<p`;
ask `ApplyTac Cases`;
ask `ApplyTac RW_TAC arith_ss NOT_PRIME_0`;
ask `ApplyTac RW_TAC arith_ss NOT_PRIME_0`;
ask `StoreTop PRIME_POS`;


(* Bail out on the following, since I can't handle complete induction in the
   current situation
(*---------------------------------------------------------------------------*
 * Every number has a prime factor, except for 1. The proof proceeds by a    *
 * "complete" induction on "n", and then considers cases on whether n is     *
 * prime or not. The first case (n is prime) is trivial. In the second case, *
 * there must be an "x" that divides n, and x is not 1 or n. By DIVIDES_LE,  *
 * n=0 or x <= n. If n=0, then 2 is a prime that divides 0. On the other     *
 * hand, if x <= n, there are two cases: if x<n then we can use the i.h. and *
 * by transitivity of divides we are done; otherwise, if x=n, then we have   *
 * a contradiction with the fact that x is not 1 or n.                       *
 *---------------------------------------------------------------------------*)

val PRIME_FACTOR = store_thm("PRIME_FACTOR",
 ``!n. ~(n = 1) ==> ?p. prime p /\ p divides n``,
INDUCT_THEN COMPLETE_INDUCTION ASSUME_TAC THEN STRIP_TAC
 THEN Cases_on `prime n` THENL
 [PROVE_TAC [DIVIDES_REFL],
  `?x. x divides n /\ ~(x=1) /\ ~(x=n)` by PROVE_TAC[prime]
    THEN PROVE_TAC [LESS_OR_EQ, PRIME_2,
                    DIVIDES_LE, DIVIDES_TRANS, DIVIDES_0]]);
*)

ask `MkThm PRIME_FACTOR !n. ~(n = 1) ==> ?p. prime p /\ p divides n`;
  
ask `Goal ~(x=0) = 0<x`;
ask `ApplyTac DECIDE_TAC`;
ask `StoreTop lem`;

ask `Goal !n. ?p. n < p /\ prime p`;
ask `ApplyTac SposeNot`;
ask `ApplyTac Have "~(FACT n + 1 = 1)" RW_TAC arith_ss lem FACT_LESS`;
ask `ApplyTac Have 
       "?p. prime p /\ p divides (FACT n + 1)" PROVE_TAC PRIME_FACTOR`;
ask `ApplyTac Have "0 < p"  PROVE_TAC PRIME_POS`;
ask `ApplyTac Have "p <= n" PROVE_TAC NOT_LESS`;
ask `ApplyTac Have "p divides FACT n" PROVE_TAC DIVIDES_FACT`;
ask `ApplyTac Have "p divides 1" PROVE_TAC DIVIDES_ADDL`;
ask `ApplyTac Have "p = 1" PROVE_TAC DIVIDES_ONE`;
ask `ApplyTac Have "~prime p" PROVE_TAC NOT_PRIME_1`;
ask `ApplyTac PROVE_TAC`;
ask `StoreTop EUCLID`;


(* At end of session *)
val _ = ask `stop.`;
val _ = Socket.close sock;
