(* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *) 
(*  Title:      Pure/IsaPlanner/isaplib.ML
    ID:		$Id: isaplib.ML,v 1.5 2005/06/02 07:11:33 wenzelm Exp $
    Author:     Lucas Dixon, University of Edinburgh
                lucasd@dai.ed.ac.uk
*)
(* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *) 
(*  DESCRIPTION:

    A few useful system-independent utilities.
*)
(* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *) 
signature ISAP_LIB =
sig
  (* ints *)
  val max : int * int -> int

  (* seq operations *)
  val ALL_BUT_LAST : ('a -> 'b Seq.seq) -> 'a -> 'b Seq.seq
  val FST : ('a -> 'b Seq.seq) -> 'a -> 'b Seq.seq
  val NTH : int -> ('a -> 'b Seq.seq) -> 'a -> 'b Seq.seq
  val all_but_last_of_seq : 'a Seq.seq -> 'a Seq.seq
  val nat_seq : int Seq.seq
  val nth_of_seq : int -> 'a Seq.seq -> 'a option
  val pair_seq : 'a Seq.seq -> 'b Seq.seq -> ('a * 'b) Seq.seq
  val seq_is_empty : 'a Seq.seq -> bool
  val number_seq : 'a Seq.seq -> (int * 'a) Seq.seq

  datatype 'a skipseqT = 
           skipmore of int
         | skipseq of 'a Seq.seq Seq.seq;
  val skipto_seqseq : int -> 'a Seq.seq Seq.seq -> 'a skipseqT
  val seqflat_seq : 'a Seq.seq Seq.seq -> 'a Seq.seq 

  (* lists *)
  val mk_num_list : int -> int list
  val number_list : int -> 'a list -> (int * 'a) list
  val repeated_list : int -> 'a -> 'a list
  val all_pairs : 'a list -> 'b list -> ('a * 'b) list
	val get_ends_of : ('a * 'a -> bool) ->
										('a * 'a) -> 'a list -> ('a * 'a)
  val flatmap : ('a -> 'b list) -> 'a list -> 'b list

	val lrem : ('a * 'b -> bool) -> 'a list -> 'b list -> 'b list
  val forall_list : ('a -> bool) -> 'a list -> bool

  (* the list of lists with one of each of the original sublists *)
  val one_of_each : 'a list list -> 'a list list

  (* type changing *)
  exception NOT_A_DIGIT of string
  val int_of_string : string -> int

  (* string manipulations *)
  val remove_end_spaces : string -> string
  val str_indent : string -> string
  val string_of_intlist : int list -> string
  val string_of_list : ('a -> string) -> 'a list -> string

  (* options *)
  val aptosome : 'a option -> ('a -> 'b) -> 'b option
  val seq_mapfilter : ('a -> 'b option) -> 'a Seq.seq -> 'b Seq.seq
  val seq_map_to_some_filter : ('a -> 'b) -> 'a option Seq.seq 
                               -> 'b Seq.seq
end;



structure IsaPLib :> ISAP_LIB = 
struct
 
(* Int *)
fun max (x,y) = if x > y then x else y;

(* Seq *)
fun seq_map_to_some_filter f s0 =
    let 
      fun recf s () = 
          case (Seq.pull s) of 
            NONE => NONE
          | SOME (NONE,s') => recf s' ()
          | SOME (SOME d, s') => 
            SOME (f d, Seq.make (recf s'))
    in Seq.make (recf s0) end;

fun seq_mapfilter f s0 =
    let 
      fun recf s () = 
          case (Seq.pull s) of 
            NONE => NONE
          | SOME (a,s') => 
            (case f a of NONE => recf s' ()
                       | SOME b => SOME (b, Seq.make (recf s')))
    in Seq.make (recf s0) end;



(* a simple function to pair with each element of a list, a number *)
fun number_list i [] = []
	| number_list i (h::t) = 
		(i,h)::(number_list (i+1) t)

(* check to see if a sequence is empty *)
fun seq_is_empty s = is_none (Seq.pull s);

(* the sequence of natural numbers *)
val nat_seq = 
      let fun nseq i () = SOME (i, Seq.make (nseq (i+1)))
      in Seq.make (nseq 1)
      end;

(* create a sequence of pairs of the elements of the two sequences
   If one sequence becomes empty, then so does the pairing of them. 
   
   This is particularly useful if you wish to perform counting or
   other repeated operations on a sequence and you want to combvine
   this infinite sequence with a possibly finite one.

   behaviour: 
   s1: a1, a2, ... an
   s2: b1, b2, ... bn ...

   pair_seq s1 s2: (a1,b1), (a2,b2), ... (an,bn)
*)
fun pair_seq seq1 seq2 = 
    let
      fun pseq s1 s2 () = 
	        case Seq.pull s1 of 
	          NONE => NONE
	        | SOME (s1h, s1t) => 
	          case Seq.pull s2 of 
		          NONE => NONE
	          | SOME (s2h, s2t) =>
		          SOME ((s1h, s2h), Seq.make (pseq s1t s2t))
    in
      Seq.make (pseq seq1 seq2)
    end;

(* number a sequence *)
fun number_seq s = pair_seq nat_seq s;

(* cuts off the last element of a sequence *)
fun all_but_last_of_seq s = 
    let 
      fun f () = 
	  case Seq.pull s of
	    NONE => NONE
	  | SOME (a, s2) => 
	    (case Seq.pull s2 of 
	       NONE => NONE
	     | SOME (a2,s3) => 
	       SOME (a, all_but_last_of_seq (Seq.cons (a2,s3))))
    in
      Seq.make f
    end;

 fun ALL_BUT_LAST r st = all_but_last_of_seq (r st);


  (* nth elem for sequenes, return none if out of bounds *)
  fun nth_of_seq i l = 
           if (seq_is_empty l) then NONE 
           else if i <= 1 then SOME (Seq.hd l)
           else nth_of_seq (i-1) (Seq.tl l);

  (* for use with tactics... gives no_tac if element isn't there *)
  fun NTH n f st = 
      let val r = nth_of_seq n (f st) in 
        if (is_none r) then Seq.empty else (Seq.single (valOf r))
      end;
 
  (* First result of a tactic... uses NTH, so if there is no elements,

     then no_tac is returned. *)
  fun FST f = NTH 1 f;

datatype 'a skipseqT = skipmore of int
  | skipseq of 'a Seq.seq Seq.seq;
(* given a seqseq, skip the first m non-empty seq's *)
fun skipto_seqseq m s = 
    let 
      fun skip_occs n sq = 
          case Seq.pull sq of 
            NONE => skipmore n
          | SOME (h,t) => 
            (case Seq.pull h of NONE => skip_occs n t
             | SOME _ => if n <= 1 then skipseq (Seq.cons (h, t))
                         else skip_occs (n - 1) t)
    in (skip_occs m s) end;

(* handy function for re-arranging Sequence operations *)
(* [[a,b,c],[d,e],...] => flatten [[a,d,...], [b,e,...], [c, ...]] *)
fun seqflat_seq ss = 
    let 
      fun pulltl LL () = 
          (case Seq.pull LL of 
             NONE => NONE
           | SOME (hL,tLL) => 
             (case Seq.pull hL of 
                NONE => pulltl tLL ()
              | SOME (h,tL) => 
                SOME (h, Seq.make (recf (tLL, (Seq.single tL))))))
      and recf (fstLL,sndLL) () = 
          (case Seq.pull fstLL of 
             NONE => pulltl sndLL ()
           | SOME (hL, tLL) => 
             (case Seq.pull hL of 
                NONE => recf (tLL, sndLL) ()
              | SOME (h,tL) => 
                SOME (h, Seq.make (recf (tLL, Seq.append (sndLL, Seq.single tL))))))
    in Seq.make (pulltl ss) end;
(* tested with: 
val ss = Seq.of_list [Seq.of_list [1,2,3], Seq.of_list [4,5], Seq.of_list [7,8,9,10]];
Seq.list_of (seqflat_seq ss);
val it = [1, 4, 7, 2, 5, 8, 3, 9, 10] : int list
*)



  (* create a list opf the form (n, n-1, n-2, ... ) *)
  fun mk_num_list n =  
      if n < 1 then [] else (n :: (mk_num_list (n-1))); 

  fun repeated_list n a =  
      if n < 1 then [] else (a :: (repeated_list (n-1) a)); 

  (* create all possible pairs with fst element from the first list
     and snd element from teh second list *)
  fun all_pairs xs ys = 
      let 
        fun all_pairs_aux yss [] _ acc = acc
          | all_pairs_aux yss (x::xs) [] acc = 
            all_pairs_aux yss xs yss acc
          | all_pairs_aux yss (xs as (x1::x1s)) (y::ys) acc = 
                               all_pairs_aux yss xs ys ((x1,y)::acc)
      in
        all_pairs_aux ys xs ys []
      end;


  (* create all possible pairs with fst element from the first list
     and snd element from teh second list *)
  (* FIXME: make tail recursive and quick *)
  fun one_of_each [] = []
    | one_of_each [[]] = []
    | one_of_each [(h::t)] = [h] :: (one_of_each [t])
    | one_of_each ([] :: xs) = []
    | one_of_each ((h :: t) :: xs) = 
      (map (fn z => h :: z) (one_of_each xs)) 
      @ (one_of_each (t :: xs));


(* function to get the upper/lower bounds of a list 
given: 
gr : 'a * 'a -> bool  = greater than check
e as (u,l) : ('a * 'a) = upper and lower bits
l : 'a list = the list to get the upper and lower bounds of
returns a pair ('a * 'a) of the biggest and lowest value w.r.t "gr"
*)
fun get_ends_of gr (e as (u,l)) [] = e
  | get_ends_of gr (e as (u,l)) (h :: t) = 
    if gr(h,u) then get_ends_of gr (h,l) t
    else if gr(l,h) then get_ends_of gr (u,h) t
    else get_ends_of gr (u,l) t;

fun flatmap f = List.concat o map f;

  (* quick removal of "rs" elements from "ls" when (eqf (r,l)) is true 
		 Ignores ordering. *)
  fun lrem eqf rs ls = 
			let fun list_remove rs ([],[]) = []
						| list_remove [] (xs,_) = xs
						| list_remove (r::rs) ([],leftovers) = 
							list_remove rs (leftovers,[])
						| list_remove (r1::rs) ((x::xs),leftovers) = 
							if eqf (r1, x) then list_remove (r1::rs) (xs,leftovers)
							else list_remove (r1::rs) (xs, x::leftovers)
			in
				list_remove rs (ls,[])
			end;

fun forall_list f [] = true
  | forall_list f (a::t) = f a orelse forall_list f t;


  (* crappy string indeter *)
  fun str_indent s = 
    implode (map (fn s => if s = "\n" then "\n  " else s) (explode s));
      

  fun remove_end_spaces s = 
      let 
				fun rem_starts [] = [] 
					| rem_starts (" " :: t) = rem_starts t
					| rem_starts ("\t" :: t) = rem_starts t
					| rem_starts ("\n" :: t) = rem_starts t
					| rem_starts l = l
				fun rem_ends l = rev (rem_starts (rev l))
      in
				implode (rem_ends (rem_starts (explode s)))
      end;

(* convert a string to an integer *)
  exception NOT_A_DIGIT of string;

  fun int_of_string s = 
      let 
				fun digits_to_int [] x = x
					| digits_to_int (h :: t) x = digits_to_int t (x * 10 + h);
	
				fun char_to_digit c = 
						case c of 
							"0" => 0
						| "1" => 1
						| "2" => 2
						| "3" => 3
						| "4" => 4
						| "5" => 5
						| "6" => 6
						| "7" => 7
						| "8" => 8
						| "9" => 9
						| _ => raise NOT_A_DIGIT c
      in
				digits_to_int (map char_to_digit (explode (remove_end_spaces s))) 0
      end;

  (* Debugging/printing code for this datatype *)
  fun string_of_list f l = 
      let 
				fun auxf [] = ""
					| auxf [a] = (f a)
					| auxf (h :: (t as (h2 :: t2))) = (f h) ^ ", " ^ (auxf t)
      in
				"[" ^ (auxf l) ^ "]"
      end;

   val string_of_intlist = string_of_list string_of_int;


  (* options *)
  fun aptosome NONE f = NONE
    | aptosome (SOME x) f = SOME (f x);

end;
