(* 
 File: Expressions.sml

 Interface to arithmetic expressions
*)

structure Expressions :> Expressions =
struct

   type id = string
   type hid = HashTable.hashedstring

   exception cannotcompare
   exception idnotintable

   datatype Aexp =  
      IDENT of string
    | NUM of real
    | INTNUM of int
    | TOP
    | PLUS of Aexp * Aexp
    | DIFF of Aexp * Aexp
    | PROD of Aexp * Aexp
    | QUOT of Aexp * Aexp
    | MIN of Aexp * Aexp
    | RATE of hid

   val expids:((id * Aexp) list) ref =
      (ref [])

   fun makeID s = s;
   fun printID s = s;
   fun sameIDs (k1:id,k2:id) = (k1 = k2);



   fun toString (IDENT s) = s
     | toString (RATE a) = "rate("^(HashTable.unhash(a))^")"
     | toString (NUM r) = Real.fmt (StringCvt.FIX NONE) r
     | toString (INTNUM i) = Int.toString i
     | toString (TOP) = "T"
     | toString (PLUS(e1,e2)) = "("^(toString e1)^"+"^(toString e2)^")"
     | toString (DIFF(e1,e2)) = "("^(toString e1)^"-"^(toString e2)^")"
     | toString (PROD(e1,e2)) = (toString e1)^"*"^(toString e2)
     | toString (QUOT(e1,e2)) = (toString e1)^"/"^(toString e2)
     | toString (MIN(e1,e2)) = "min("^(toString e1)^","^(toString e2)^")";

   fun aequals (IDENT s1, IDENT s2) = s1 = s2
     | aequals (NUM r1, NUM r2) = (((r1 - r2) > ~0.0001) andalso ((r1 - r2) < 0.0001))
     | aequals (RATE a1, RATE a2) = a1 = a2
     | aequals (INTNUM r1, INTNUM r2) = r1 = r2
     | aequals (TOP, TOP) = true
     | aequals (PLUS(a1,b1), PLUS(a2,b2)) =
       aequals(a1,a2) andalso aequals(b1,b2)
     | aequals (DIFF(a1,b1), DIFF(a2,b2)) =
       aequals(a1,a2) andalso aequals(b1,b2)
     | aequals (PROD(a1,b1), PROD(a2,b2)) =
       aequals(a1,a2) andalso aequals(b1,b2)
     | aequals (QUOT(a1,b1), QUOT(a2,b2)) =
       aequals(a1,a2) andalso aequals(b1,b2)
     | aequals (MIN(a1,b1), MIN(a2,b2)) =
       aequals(a1,a2) andalso aequals(b1,b2);


   fun simple (IDENT s) = true
     | simple (RATE a) = true
     | simple (NUM r) = true
     | simple (INTNUM i) = true
     | simple (TOP) = true
     | simple _ = false;

   fun flat (IDENT s) = true
     | flat (RATE r) = true
     | flat (NUM r) = true
     | flat (INTNUM i) = true
     | flat (TOP) = true
     | flat (PLUS(e1,e2)) = (simple e1) andalso (simple e2)
     | flat (DIFF(e1,e2)) = (simple e1) andalso (simple e2)
     | flat (PROD(e1,e2)) = (simple e1) andalso (simple e2)
     | flat (QUOT(e1,e2)) = (simple e1) andalso (simple e2)
     | flat (MIN(e1,e2)) = (simple e1) andalso (simple e2);


   fun stripTops2 (PROD(e1,TOP)) (PROD(e2,TOP)) =
      let
	 val (me1,e3,e4) = stripTops2 e1 e2
      in
	 ((fn e => (PROD((me1 e), TOP))), e3, e4)
      end
     | stripTops2 (PROD(e1,TOP)) (QUOT(TOP,e2)) =
       let
	  val (me1,e3,e4) = stripTops2 e1 (QUOT(INTNUM 1, e2))
       in
	  ((fn e => (PROD((me1 e), TOP))), e3, e4)
       end
     | stripTops2 (QUOT(TOP, e1)) (PROD(e2, TOP)) =
       let
	  val (me1,e3,e4) = stripTops2 (QUOT(INTNUM 1, e1)) e2
       in
	  ((fn e => (PROD((me1 e), TOP))), e3, e4)
       end
     | stripTops2 (QUOT(TOP,e1)) (QUOT(TOP,e2)) =
       let
	  val (me1,e3,e4) = stripTops2 (QUOT(INTNUM 1,e1)) (QUOT(INTNUM 1,e2))
       in
	  ((fn e => (PROD((me1 e), TOP))), e3, e4)
       end
     | stripTops2 (QUOT(e1,TOP)) (QUOT(e2,TOP)) =
       let
	  val (me1,e3,e4) = stripTops2 e1 e2
       in
	  ((fn e => (QUOT((me1 e), TOP))), e3, e4)
       end
     | stripTops2 TOP TOP =
       ((fn e => (PROD(e,TOP))), INTNUM 1, INTNUM 1)
     | stripTops2 TOP (PROD(e1,TOP)) =
       ((fn e => (PROD(e,TOP))), INTNUM 1, e1)
     | stripTops2 TOP (QUOT(TOP,e1)) =
       ((fn e => (PROD(e,TOP))), INTNUM 1, (QUOT(INTNUM 1,e1)))
     | stripTops2 (PROD(e1,TOP)) TOP =
       ((fn e => (PROD(e,TOP))), e1, INTNUM 1)
     | stripTops2 (QUOT(TOP,e1)) TOP =
       ((fn e => (PROD(e,TOP))), (QUOT(INTNUM 1,e1)), INTNUM 1)
(*      | stripTops2 e1 TOP = (stripTops2 e1 (PROD(INTNUM 1, TOP))) *)
(*      | stripTops2 TOP e2 = (stripTops2 (PROD(INTNUM 1, TOP)) e2) *)
     | stripTops2 e1 e2 = ((fn e => e), e1, e2)

   and

      stripTops e1 e2 =
      let
	 val ans = stripTops2 e1 e2
      in
	 ans
      end;


   (* try to make any Ts appear outermost *)

   fun factorTops (e as (IDENT s)) = e
     | factorTops (e as (RATE a)) = e
     | factorTops (e as (NUM r)) = e
     | factorTops (e as (INTNUM i)) = e
     | factorTops (TOP) = TOP
     | factorTops (PLUS(e1,e2)) =
       let
	  val e1s = factorTops (simplify e1)
	  val e2s = factorTops (simplify e2)
       in
	  case (e1s, e2s) of
	     (* assume TOP has been factored to RHS side *)
	     (PROD(e3, TOP), PROD(e4, TOP)) => (PROD((factorTops (PLUS(e3,e4))), TOP))
	   | (PROD(e3, TOP), TOP) => (PROD((factorTops (PLUS(e3, INTNUM 1)), TOP)))
	   | (PROD(e3, TOP), QUOT(TOP, e4)) => (PROD((factorTops (PLUS(e3,(factorTops (QUOT(INTNUM 1,e4)))))), TOP))
		
	   | (QUOT(e3, TOP), QUOT(e4, TOP)) => (QUOT((factorTops (PLUS(e3,e4))), TOP))
	   | (QUOT(e3, TOP), TOP) => (QUOT((factorTops (PLUS(e3, INTNUM 1))), TOP))
	   | (QUOT(TOP, e3), PROD(TOP, e4)) => (PROD((factorTops (PLUS(e4, (factorTops (QUOT(INTNUM 1,e3)))))), TOP))
		
	   | (TOP, PROD(e4, TOP)) => (PROD((factorTops (PLUS(e4, INTNUM 1))), TOP))
	   | (TOP, TOP) => (PROD(INTNUM 2, TOP))
	   | (TOP, QUOT(TOP, e4)) => (PROD((factorTops (PLUS(INTNUM 1,(factorTops (QUOT(INTNUM 1,e4)))))), TOP))
	   | (TOP, e3) => PLUS(e3, TOP)
		
	   | (PLUS(e3, TOP), e4) => PLUS((factorTops (PLUS(e3,e4))), TOP)
	   | (PLUS(TOP, e3), e4) => PLUS((factorTops (PLUS(e3,e4))), TOP)
		
	   | (DIFF(e3, TOP), TOP) => e3
		
	   | (e3, PLUS(e4, TOP)) => PLUS((factorTops (PLUS(e3,e4))), TOP)
	   | (e3, DIFF(e4, TOP)) => DIFF((factorTops (PLUS(e3,e4))), TOP)
	   | (e3, e4) => PLUS(e3, e4)
       end
    
     | factorTops (DIFF(e1,e2)) =
       let
	  val e1s = factorTops (simplify e1)
	  val e2s = factorTops (simplify e2)
       in
	  case (e1s, e2s) of
	     (* assume TOP has been factored to RHS side *)
	     (PROD(e3, TOP), PROD(e4, TOP)) => (PROD((factorTops (DIFF(e3,e4))), TOP))
	   | (PROD(e3, TOP), TOP) => (PROD((factorTops (DIFF(e3, INTNUM 1))), TOP))
	   | (PROD(e3, TOP), QUOT(TOP, e4)) => (PROD((factorTops (DIFF(e3,(factorTops (QUOT(INTNUM 1,e4)))))), TOP))
		
	   | (QUOT(e3, TOP), QUOT(e4, TOP)) => (QUOT((factorTops (DIFF(e3,e4))), TOP))
	   | (QUOT(e3, TOP), TOP) => (QUOT((factorTops (DIFF(e3, INTNUM 1))), TOP))
	   | (QUOT(TOP, e3), PROD(TOP, e4)) => (PROD((factorTops (DIFF((factorTops (QUOT(INTNUM 1,e3))), e4))), TOP))
		
	   | (TOP, PROD(e4, TOP)) => (PROD((factorTops (DIFF(INTNUM 1, e4))), TOP))
	   | (TOP, TOP) => INTNUM 0
	   | (TOP, QUOT(TOP, e4)) => (PROD((factorTops (DIFF(INTNUM 1,(factorTops (QUOT(INTNUM 1,e4)))))), TOP))
	   | (TOP, e3) => DIFF(e3, TOP)
		
	   | (PLUS(e3, TOP), e4) => PLUS((factorTops (DIFF(e3,e4))), TOP)
	   | (PLUS(TOP, e3), e4) => PLUS((factorTops (DIFF(e3,e4))), TOP)
		
	   | (DIFF(e3, TOP), TOP) => factorTops (DIFF(e3, (PROD(INTNUM 2, TOP))))
		
	   | (e3, PLUS(e4, TOP)) => DIFF((factorTops (DIFF(e3,e4))), TOP)
	   | (e3, DIFF(e4, TOP)) => PLUS((factorTops (DIFF(e3,e4))), TOP)
	   | (e3, e4) => DIFF(e3, e4)
       end
    
     | factorTops (PROD(e1,e2)) =
       let
	  val e1s = factorTops (simplify e1)
	  val e2s = factorTops (simplify e2)
       in
	  case (e1s, e2s) of
	     (* assume TOP has been factored to RHS side *)
	     (PROD(e3, TOP), PROD(e4, TOP)) => PROD(PROD((factorTops (PROD(e3,e4))), TOP), TOP)
	   | (PROD(e3, TOP), QUOT(TOP, e4)) => PROD(PROD((factorTops (QUOT(e3,e4))), TOP), TOP)
	   | (PROD(e3, TOP), QUOT(e4, TOP)) => (factorTops (PROD(e3,e4)))
	   | (PROD(e3, TOP), e4) => PROD((factorTops (PROD(e3, e4))), TOP)
		
	   | (QUOT(e3, TOP), PROD(e4, TOP)) => (factorTops (PROD(e3,e4)))
	   | (QUOT(e3, TOP), QUOT(TOP, e4)) => (factorTops (QUOT(e3,e4)))
	   | (QUOT(e3, TOP), QUOT(e4, TOP)) => QUOT(QUOT((factorTops (PROD(e3,e4))), TOP), TOP)
	   | (QUOT(e3, TOP), TOP) => e3
	   | (QUOT(e3, TOP), e4) => QUOT((factorTops (PROD(e3,e4))), TOP)
		
	   | (TOP, QUOT(e4, TOP)) => e4
	   | (TOP, e4) => (factorTops (PROD(e4, TOP)))
		
	   | (e3, PROD(e4, TOP)) => PROD((factorTops (PROD(e3,e4))), TOP)
	   | (e3, QUOT(e4, TOP)) => QUOT((factorTops (PROD(e3,e4))), TOP)
	   | (e3, QUOT(TOP, e4)) => PROD((factorTops (QUOT(e3,e4))), TOP)
	   | (e3, e4) => PROD(e3, e4)
       end
    
     | factorTops (QUOT(e1,e2)) =
       let
	  val e1s = factorTops (simplify e1)
	  val e2s = factorTops (simplify e2)
       in
	  case (e1s, e2s) of
	     (* assume TOP has been factored to RHS side *)
	     (PROD(e3, TOP), PROD(e4, TOP)) => (factorTops (QUOT(e3,e4)))
	   | (PROD(e3, TOP), QUOT(TOP, e4)) => (factorTops (PROD(e3,e4)))
	   | (PROD(e3, TOP), QUOT(e4, TOP)) => PROD(PROD((factorTops (QUOT(e3,e4))), TOP), TOP)
	   | (PROD(e3, TOP), TOP) => e3
	   | (PROD(e3, TOP), e4) => PROD((factorTops (QUOT(e3, e4))), TOP)
		
	   | (QUOT(e3, TOP), PROD(e4, TOP)) => QUOT(QUOT((factorTops (QUOT(e3,e4))), TOP), TOP)
	   | (QUOT(e3, TOP), QUOT(TOP, e4)) => QUOT(QUOT((factorTops (PROD(e3,e4))), TOP), TOP)
	   | (QUOT(e3, TOP), QUOT(e4, TOP)) => (factorTops (QUOT(e3,e4)))
	   | (QUOT(e3, TOP), e4) => QUOT((factorTops (QUOT(e3,e4))), TOP)
		
	   | (TOP, QUOT(TOP, e4)) => e4
	   | (TOP, TOP) => INTNUM 1
		
	   | (e3, PROD(e4, TOP)) => QUOT((factorTops (PROD(e3,e4))), TOP)
	   | (e3, QUOT(e4, TOP)) => PROD((factorTops (QUOT(e3,e4))), TOP)
	   | (e3, QUOT(TOP, e4)) => QUOT((factorTops (PROD(e3,e4))), TOP)
	   | (e3, e4) => QUOT(e3, e4)
       end    
     | factorTops (MIN(e1,e2)) =
       let
	  val e1s = factorTops (simplify e1)
	  val e2s = factorTops (simplify e2)
       in
	  case (e1s, e2s) of
	     (* assume TOP has been factored to RHS side *)
	     (PROD(e3, TOP), PROD(e4, TOP)) => PROD((factorTops (MIN(e3,e4))), TOP)
	   | (PROD(e3, TOP), QUOT(TOP, e4)) => PROD((factorTops (MIN(e3, (QUOT(INTNUM 1, e4))))), TOP)
	   | (PROD(e3, TOP), TOP) => PROD((factorTops (MIN(e3,INTNUM 1))), TOP)
		
	   | (QUOT(e3, TOP), QUOT(e4, TOP)) => QUOT((factorTops (MIN(e3,e4))), TOP)
		
	   | (TOP, PROD(e4, TOP)) => PROD((factorTops (MIN(INTNUM 1, e4))), TOP)
	   | (TOP, QUOT(TOP, e4)) => PROD((factorTops (MIN(INTNUM 1, (factorTops (QUOT(INTNUM 1, e4)))))), TOP)
	   | (TOP, TOP) => TOP
		
	   | (e3, MIN(e4, TOP)) => MIN((factorTops (MIN(e3,e4))), TOP)
	   | (e3, e4) => MIN(e3, e4)
       end

   and
      
      simplify2 (PLUS(e1,e2)) =
       let
	  val e1f = factorTops e1
	  val e2f = factorTops e2
	  val (me,e1f2,e2f2) = stripTops e1f e2f
	  val e1s = simplify e1f2
	  val e2s = simplify e2f2
       in
	  (me
	   (case (e1s, e2s) of
	       (INTNUM 0, e) => e
	     | (e, INTNUM 0) => e
	     | _ => (PLUS(e1s,e2s))))
       end

     | simplify2 (DIFF(e1,e2)) =
       let
	  val e1f = factorTops e1
	  val e2f = factorTops e2
	  val (me,e1f2,e2f2) = stripTops e1f e2f
	  val e1s = simplify e1f2
	  val e2s = simplify e2f2
       in
	  (me
	   (case (e1s, e2s) of
	       (INTNUM 0, e) => e
	     | (e, INTNUM 0) => e
	     | _ => (DIFF(e1s,e2s))))
       end

     | simplify2 (PROD(e1,e2)) =
       let
	  val e1f = factorTops e1
	  val e2f = factorTops e2
	  val (me,e1f2,e2f2) = stripTops e1f e2f
	  val e1s = simplify e1f2
	  val e2s = simplify e2f2
       in
	  (me
	   (case (e1s, e2s) of
	       (INTNUM 0, e) => (INTNUM 0)
	     | (e, INTNUM 0) => (INTNUM 0)
	     | (INTNUM 1, e) => e
	     | (e, INTNUM 1) => e
	     | _ => (PROD(e1s,e2s))))
       end

     | simplify2 (QUOT(e1,e2)) =
       let
	  val e1f = factorTops e1
	  val e2f = factorTops e2
	  val (me,e1f2,e2f2) = stripTops e1f e2f
	  val e1s = simplify e1f2
	  val e2s = simplify e2f2
       in
	  (me
	   (case (e1s, e2s) of
	       (INTNUM 0, e) => (INTNUM 0)
	     | (e, INTNUM 1) => e
	     | _ => (QUOT(e1s,e2s))))
       end

     | simplify2 (MIN(e1,e2)) =
       let
	  val e1f = factorTops e1
	  val e2f = factorTops e2
	  val (me,e1f2,e2f2) = stripTops e1f e2f
	  val e1s = simplify e1f2
	  val e2s = simplify e2f2
       in
	  (me
	   (case (e1s, e2s) of
	       (INTNUM x, INTNUM y) => if (x < y) then e1s else e2s
	     | (NUM x, NUM y) => if (x < y) then e1s else e2s
	     | (NUM x, INTNUM y) => if (x < (Real.fromInt y)) then e1s else e2s
	     | (INTNUM x, NUM y) => if ((Real.fromInt x) < y) then e1s else e2s
	     (* we know at this stage that both sides can't both have TOP *)
	     (* factors, since we've called factorTops above *)
	     | (PROD(e1,TOP), e2) => e2
	     | (QUOT(TOP,e1), e2) => e2
	     | (e1, PROD(e2,TOP)) => e1
	     | (e1, QUOT(TOP,e2)) => e1
	     (* likewise here, we can't have TOP and another TOP factor *)
	     | (e1, TOP) => e1
	     | (TOP, e1) => e1
	     | _ => (MIN(e1s,e2s))))
       end

     | simplify2 e = e

   and
      
      simplify e =
      let
	 val ans = simplify2 e
      in
	 ans
      end;
	 

   fun evaluate (e as (IDENT s)) = e
     | evaluate (e as (RATE a)) = e
     | evaluate (e as (NUM r)) = e
     | evaluate (e as (INTNUM i)) = e
     | evaluate (e as (TOP)) = e
     | evaluate (e as (PLUS(e1,e2))) = 
       let
	  val e1e = evaluate e1
	  val e2e = evaluate e2
       in
	  case (e1e, e2e) of
	     (NUM r1, NUM r2) => NUM(r1+r2)
	   | (INTNUM r1, INTNUM r2) => INTNUM(r1+r2)
	   | (INTNUM r1, NUM r2) => NUM(Real.fromInt(r1)+r2)
	   | (NUM r1, INTNUM r2) => NUM(r1+Real.fromInt(r2))
	   | _ => PLUS(e1e,e2e)
       end
     | evaluate (e as (DIFF(e1,e2))) = 
       let
	  val e1e = evaluate e1
	  val e2e = evaluate e2
       in
	  case (e1e, e2e) of
	     (NUM r1, NUM r2) => NUM(r1-r2)
	   | (INTNUM r1, INTNUM r2) => INTNUM(r1-r2)
	   | (INTNUM r1, NUM r2) => NUM(Real.fromInt(r1)-r2)
	   | (NUM r1, INTNUM r2) => NUM(r1-Real.fromInt(r2))
	   | _ => DIFF(e1e,e2e)
       end
     | evaluate (e as (PROD(e1,e2))) = 
       let
	  val e1e = evaluate e1
	  val e2e = evaluate e2
       in
	  case (e1e, e2e) of
	     (NUM r1, NUM r2) => NUM(r1*r2)
	   | (INTNUM r1, INTNUM r2) => INTNUM(r1*r2)
	   | (INTNUM r1, NUM r2) => NUM(Real.fromInt(r1)*r2)
	   | (NUM r1, INTNUM r2) => NUM(r1*Real.fromInt(r2))
	   | _ => PROD(e1e,e2e)
       end
     | evaluate (e as (QUOT(e1,e2))) = 
       let
	  val e1e = evaluate e1
	  val e2e = evaluate e2
       in
	  case (e1e, e2e) of
	     (NUM r1, NUM r2) => NUM((r1:real) / r2)
	   | (INTNUM r1, INTNUM r2) =>
		if (((r1 div r2) * r2) = r1) then
		   INTNUM(r1 div r2)
		else
		   NUM(Real.fromInt(r1) / Real.fromInt(r2))
	   | (INTNUM r1, NUM r2) => NUM(Real.fromInt(r1) / r2)
	   | (NUM r1, INTNUM r2) => NUM(r1 / Real.fromInt(r2))
	   | _ => QUOT(e1e,e2e)
       end
     | evaluate (e as (MIN(e1,e2))) = 
       let
	  val e1e = evaluate e1
	  val e2e = evaluate e2
       in
	  case (e1e, e2e) of
	     (NUM r1, NUM r2) => if (r1<r2) then e1e else e2e
	   | (INTNUM r1, INTNUM r2) => if (r1<r2) then e1e else e2e
	   | (INTNUM r1, NUM r2) => if (Real.fromInt(r1)<r2) then e1e else e2e
	   | (NUM r1, INTNUM r2) => if (r1<Real.fromInt(r2)) then e1e else e2e
	   | _ => MIN(e1e,e2e)
       end



   (* this might not terminate!! *)
(*    fun subids (IDENT s) = subids (Identifiers.getExp s) *)
(*      | subids (e as (NUM r)) = e *)
(*      | subids (e as (INTNUM i)) = e *)
(*      | subids (e as (TOP)) = e *)
(*      | subids (e as (PLUS(e1,e2))) = (PLUS(subids e1, subids e2)) *)
(*      | subids (e as (DIFF(e1,e2))) = (DIFF(subids e1, subids e2)) *)
(*      | subids (e as (PROD(e1,e2))) = (PROD(subids e1, subids e2)) *)
(*      | subids (e as (QUOT(e1,e2))) = (QUOT(subids e1, subids e2)) *)
(*      | subids (e as (MIN(e1,e2))) = (MIN(subids e1, subids e2)) *)


   fun lessThan (IDENT s1, IDENT s2) = s1 < s2
     | lessThan (IDENT _, _) = true
     | lessThan (_, IDENT _) = false
     | lessThan (RATE s1, RATE s2) = String.<(HashTable.unhash(s1),HashTable.unhash(s2))
     | lessThan (RATE _, _) = true
     | lessThan (_, RATE _) = false
     | lessThan (NUM r1, NUM r2) = r1 < r2
     | lessThan (NUM _, _) = true
     | lessThan (_, NUM _) = false
     | lessThan (INTNUM r1, INTNUM r2) = r1 < r2
     | lessThan (INTNUM _, _) = true
     | lessThan (_, INTNUM _) = false
     | lessThan (TOP, _) = true
     | lessThan (_, TOP) = false
     | lessThan (PLUS(a1,b1), PLUS(a2,b2)) =
       lessThan(a1,a2) orelse ((aequals(a1,a2)) andalso lessThan(b1,b2))
     | lessThan (PLUS(a1,b1), _) = true
     | lessThan (_, PLUS(a1,b1)) = false
     | lessThan (DIFF(a1,b1), DIFF(a2,b2)) =
       lessThan(a1,a2) orelse ((aequals(a1,a2)) andalso lessThan(b1,b2))
     | lessThan (DIFF(a1,b1), _) = true
     | lessThan (_, DIFF(a1,b1)) = false
     | lessThan (PROD(a1,b1), PROD(a2,b2)) =
       lessThan(a1,a2) orelse ((aequals(a1,a2)) andalso lessThan(b1,b2))
     | lessThan (PROD(a1,b1), _) = true
     | lessThan (_, PROD(a1,b1)) = false
     | lessThan (QUOT(a1,b1), QUOT(a2,b2)) =
       lessThan(a1,a2) orelse ((aequals(a1,a2)) andalso lessThan(b1,b2))
     | lessThan (QUOT(a1,b1), _) = true
     | lessThan (_, QUOT(a1,b1)) = false
     | lessThan (MIN(a1,b1), MIN(a2,b2)) =
       lessThan(a1,a2) orelse ((aequals(a1,a2)) andalso lessThan(b1,b2));
(*      | lessThan (MIN(a1,b1), _) = true *)
(*      | lessThan (_, MIN(a1,b1)) = false *)

   fun printExpIDs () = 
      (map
      (fn (k,d) =>
       let
	  val ks = printID k
	  val ds = toString d
       in
	  TextIO.output(TextIO.stdOut, "id "^ks^" is "^ds^"\n")
       end)
      (!expids);());

   fun getThing k [] = raise idnotintable
     | getThing k ((k2,d)::t) = 
       if (k=k2) then
	  d
       else
	  getThing k t

   fun addExpID k d =
      ((* TextIO.output(TextIO.stdOut, "adding "^(printID k)^" which equals " *)
(* 		     ^(toString d)^"\n"); *)
(*        TextIO.flushOut(TextIO.stdOut); *)
       (expids := (k,d)::(!expids)));

   fun getExp k = getThing k (!expids)

   fun subForIDs (PLUS(e1,e2)) = (PLUS((subForIDs e1), (subForIDs e2)))
     | subForIDs (DIFF(e1,e2)) = (DIFF((subForIDs e1), (subForIDs e2)))
     | subForIDs (PROD(e1,e2)) = (PROD((subForIDs e1), (subForIDs e2)))
     | subForIDs (QUOT(e1,e2)) = (QUOT((subForIDs e1), (subForIDs e2)))
     | subForIDs (MIN(e1,e2)) = (MIN((subForIDs e1), (subForIDs e2)))
     | subForIDs (e as (IDENT s)) =
      ((getExp(makeID(s)))
       handle _ => e)
     | subForIDs x = x;
	 
   val eval = evaluate o simplify o subForIDs

   fun lt' (NUM r1, NUM r2) = r1 < r2
     | lt' (INTNUM r1, INTNUM r2) = r1 < r2
     | lt' (INTNUM r1, NUM r2) = (Real.fromInt(r1)) < r2
     | lt' (NUM r1, INTNUM r2) = r1 < (Real.fromInt(r2))
     | lt' (TOP, TOP) = false
     | lt' _ = raise cannotcompare

   fun lt (e1,e2) = lt' (eval e1, eval e2)

   fun filter f [] = []
     | filter f (h::t) = if (f h) then (h::(filter f t)) else (filter f t)


end;





