(*
 Code from the 3rd lecture.
*)

(* ************** Datatypes from previous lecture **************** *)

datatype Tree = Tip | Node of Tree * int * Tree;

datatype Exp
  = Const of int
  | Add of Exp * Exp
  | Mult of Exp * Exp
  | Sub of Exp * Exp;


datatype Token
  = Id of string
  | Plus
  | Times
  | Eql
  | Int of int
  | Illegal;

datatype RE
  = Empty
  | Union of RE * RE
  | Concat of RE * RE
  | Star of RE
  | C of char;

datatype Label = Epsilon | Char of char;
type Start = int;
type Finish = int;
datatype Edge = Edge of Start * Label * Finish;


val (ex6 as (start,finish,edges)) =
  (8,15,
   [Edge (9,Epsilon,10),Edge (8,Epsilon,0),Edge (8,Epsilon,6),
    Edge (1,Epsilon,9),Edge (7,Epsilon,9),Edge (0,Char #"+",1),
    Edge (6,Epsilon,2),Edge (6,Epsilon,4),Edge (3,Epsilon,7),
    Edge (5,Epsilon,7),Edge (2,Char #"-",3),Edge (4,Epsilon,5),
    Edge (11,Epsilon,14),Edge (10,Char #"D",11),Edge (14,Epsilon,12),
    Edge (13,Epsilon,15),Edge (14,Epsilon,15),Edge (15,Epsilon,14),
    Edge (12,Char #"D",13)]);

(* **************** Sets as lists ********************** *)
(* represent a set as an ordered list without duplicates *)

fun mem x [] = false
  | mem x (y::ys) =
     if x=y then true
            else mem x ys;

fun setAdd x [] = [x]
  | setAdd x (y::ys) =
      case Int.compare (x,y) of
        EQUAL => (y::ys)
      | LESS => x::y::ys
      | GREATER => y :: setAdd x ys;

fun setUnion [] [] = []
  | setUnion [] ys = ys
  | setUnion xs [] = xs
  | setUnion (x::xs) (y::ys) =
     case Int.compare (x,y) of
        EQUAL => setUnion xs (y::ys)
      | LESS =>  x:: setUnion xs (y::ys)
      | GREATER => y :: setUnion (x::xs) ys;

fun setConcat [] = []
  | setConcat (x::xs) = setUnion x (setConcat xs);

(* Turn a list into a set, sort and remove duplicates. *)
fun sort [] = []
  | sort (x::xs) = setAdd x (sort xs);

fun remDupFromOrdered [] = []
  | remDupFromOrdered [x] = [x]
  | remDupFromOrdered (x::y::zs) =
      if x=y then remDupFromOrdered (y::zs)
             else x:: remDupFromOrdered (y::zs);

fun norm xs = remDupFromOrdered(sort xs);

fun setToString xs =
  let fun help [] = "]\n"
        | help [x] = Int.toString x ^ "]\n"
        | help (x::xs) = Int.toString x ^ "," ^ help xs
  in "[" ^ help xs end


(* ********************************************************** *)

fun oneStep n (Edge(s,Epsilon,f)) =
       if n=s then [f] else []
  | oneStep n (Edge(s,Char _,f)) = []

fun oneStepFromEdges es n =
  setConcat(map (oneStep n) es);

fun oneStepFromSet es states =
   setConcat (map (oneStepFromEdges es) states);


fun eclose edges states =
    let val new = oneStepFromSet edges states
        val union = setUnion new states
    in if union = states
          then states
          else ( print (setToString states)
               ; print (setToString new)
               ; print (setToString union)
               ; print "-----------------------\n"
               ; eclose edges union )
    end;


fun fix f init =
  let val new = f init
  in if new=init then new else fix f new end;

fun eclose2 edges xs =
   let fun step x = setUnion x (oneStepFromSet edges x)
   in fix step xs end;


(* ************************************************ *)

fun transitionOn c states edges =
  let fun good (Edge(s,Char x,f)) = (c=x) andalso (mem s states)
        | good _ = false
      fun finish (Edge(s,_,f)) = f
  in map finish (List.filter good edges) end;


fun nfa edges final states [] = mem final states
  | nfa edges final states (c::cs) =
     let val _ = print ("State = "^setToString states)
         val _ = print ("Input = "^implode(c::cs)^"\n")
         val new = eclose2 edges (transitionOn c states edges)
         val _ = print ("On '"^implode [c]^"' we can get to "^setToString new)
     in if new = []
           then false
           else nfa edges final new cs
     end;



fun accept (start,final,edges) input =
    nfa edges final
       (eclose2 edges [start])
       (explode input)

(* *******************************************************888 *)

fun innerWhile x [] ans = ans
  | innerWhile x (y::ys) ans = innerWhile x ys ((x,y)::ans);

fun outerWhile ys [] ans = ans
  | outerWhile ys (x::xs) ans = outerWhile ys xs (innerWhile x ys ans);

fun cross xs ys = outerWhile ys xs [];


val ex8 =  map (fn (x,y) => (x,y)) (cross [1,2,3,4] [true,false]);

(*
[(4,false),(4,true)
,(3,false),(3,true)
,(2,false),(2,true)
,(1,false) (1,true)] : (int * bool) list

*)


(* 
*)