(*---------------------------------------------------------------------------*
 * A CoreProofEngine tester. This file can be used to test either the        *
 * Internet version of the Core Proof Engine (./CPEinet) or the Unix         *
 * version (./CPEunix).                                                      *
 *                                                                           *
 * The main difference between the two versions is that a CPEinet process    *
 * can be accessed on a different machine, while a CPEunix process needs to  * 
 * be accessed on the machine it is started on.
 *                                                                           *
 * Start up a version of mosml that knows about sockets via                  *
 *                                                                           *
     mosml -I ../sockets
     app load ["Mosml", "SocketUtils"];  quotation := true;
 *                                                                           *
 *  and then go through this file.                                           *
 *---------------------------------------------------------------------------*)


(*---------------------------------------------------------------------------*
 * First, we have to get connected to a server. There are two ways to do     *
 * this: connect to an already running one, or create a new one and then     *
 * connect to it.                                                            *
 *                                                                           *
 * To *connect* to an already-running server: execute one of the following   *
 * two "val" bindings (changing the arguments as necessary for your local    *
 * setup). 
 *                                                                           *
     (*INET*) val sock = SocketUtils.findInetServer "127.0.0.1" 5436;
     (*UNIX*) val sock = SocketUtils.findUnixServer "/tmp/foo";
 *                                                                           *
 * To *create* a server and connect to it, execute one of the following two  *
 * bindings (changing the arguments as necessary for your local setup).      *

     (*INET*) val sock = SocketUtils.createInetServer       
                   {archOS="x86-linux", HOLDIR="/home/kxs/hol98"}
                   "./CPEinet" "127.0.0.1" 5436 30;

     (*UNIX*) val (sock,file) = SocketUtils.createUnixServer 
                   {archOS="x86-linux", HOLDIR="/home/kxs/hol98"}
                   "./CPEunix" 30;
 
 * Note that the invocation of "createInetServer" will only create a         *
 * server on the current machine. If you want to start a CPEinet on a        *
 * different machine, follow the instructions at the head of CPEinet.sml.    *
 *                                                                           *
 * Note that the created servers will not die when the MoscowML session      *
 * ends, so you will have to manually hunt them down and kill them. I hope   *
 * this can be fixed soon. In the case of Unix-domain sockets, you will      *
 * also have to remove the relevant file from /tmp. This can be done by      *
 * invoking "end_session", defined below.                                    *
 *---------------------------------------------------------------------------*)


(*---------------------------------------------------------------------------*
 * A simple query mechanism for being an interactive client in mosml.        *
 *---------------------------------------------------------------------------*)
val ask = SocketUtils.ask sock;
val end_client = SocketUtils.end_client sock;
val rel_client = SocketUtils.rel_client sock;

fun end_unix_session file = 
     (end_client(); Process.system ("rm -f "^file);
      Utils.stdOutput"Don't forget to kill server process!\n");
(*UNIX*) fun end_session() = end_unix_session file;


(*---------------------------------------------------------------------------*
 *          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`; 
ask `FOL x + y = y + x`;   (* Can't prove without commutativity *) 
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 `ted`; 

(*---------------------------------------------------------------------------*
 * 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 
   currently.
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: this just ends the client-side; the server starts      *
 * waiting for another client to connect.                                    *
 *---------------------------------------------------------------------------*)
val _ = end_client();


(* Now start another session. *)

(*INET*)val sock = SocketUtils.findInetServer "127.0.0.1" 5436;
(*UNIX*)val sock = SocketUtils.findUnixServer file;
val ask  = SocketUtils.ask sock;
val end_client = SocketUtils.end_client sock;

ask `Arith !x y z. x < y /\ y < z ==> x < z`;
ask `Arith !x y z. x < y /\ y < z ==> 1 < z-x`;
val _ = end_client();

(* Don't forget to kill server, and if necessary, clean up /tmp! *)
