(*
 *)

use "../assignments/assign4Prelude.html";

val a = Char #"a"
val b = Char #"b"
val e = Epsilon

val nfa1 = map Edge
   [(0,e,1),(0,e,3),(0,e,7),(0,e,9)
   ,(1,a,2)
   ,(3,a,4)
   ,(7,a,7),(7,b,8)
   ,(9,a,10)
   ,(4,b,5)
   ,(5,b,6)
   ,(8,b,8)
   ,(10,b,11)
   ,(11,a,12)
   ,(12,b,13)]


fun edgesOnFrom c n edges =
  let fun p (Edge(s,cc,f)) = s=n andalso c=cc
  in filter p edges end;

fun nodesOnFrom c n es =
  let fun final (Edge(s,e,f)) = f
  in map final (edgesOnFrom c n es) end;

fun nodesOnFromMany c xs es = concat(map (fn x => nodesOnFrom c x es) xs)

fun oneStep es xs = norm(xs @ nodesOnFromMany Epsilon xs es);

fun eclosure edges xs =
   let val ys = oneStep edges xs
   in if xs=ys then xs else eclosure edges ys end;

fun arcs ns edges =
  let fun p (Edge(s,Char c,f)) = exists (fn x => x=s) ns
        | p _ = false
      fun char (Edge(s,Char c,f)) = c
  in map char (filter p edges) end;

fun sigma edges =
  let fun p (Edge(s,Char c,f)) = [c]
        | p _ = []
  in concat (map p edges) end;


fun nodup [] = []
  | nodup [x] = [x]
  | nodup(x::xs) = if exists (fn y => y=x) xs
                      then nodup xs
                      else x::(nodup xs);


val chars = nodup(sigma nfa1);

val s0 = eclosure nfa1 [0]
val s0a = nodesOnFromMany (Char #"a") s0 nfa1;
val s0b = nodesOnFromMany (Char #"b") s0 nfa1;
val s8 = eclosure nfa1 s0b
val s2 = eclosure nfa1 s0a
val s8a = nodesOnFromMany (Char #"a") s8 nfa1;
val s8b = nodesOnFromMany (Char #"b") s8 nfa1;
val s2a = nodesOnFromMany (Char #"a") s2 nfa1;
val s2b = nodesOnFromMany (Char #"b") s2 nfa1;
val s7 = eclosure nfa1 s2a
val s5 = eclosure nfa1 s2b
val s7a = nodesOnFromMany (Char #"a") s7 nfa1;
val s7b = nodesOnFromMany (Char #"b") s7 nfa1;
val s5a = nodesOnFromMany (Char #"a") s5 nfa1;
val s5b = nodesOnFromMany (Char #"b") s5 nfa1;
val s12 = eclosure nfa1 s5a;
val s6 =  eclosure nfa1 s5b;
val s6a = nodesOnFromMany (Char #"a") s6 nfa1;
val s6b = nodesOnFromMany (Char #"b") s6 nfa1;
val s12a = nodesOnFromMany (Char #"a") s12 nfa1;
val s12b = nodesOnFromMany (Char #"b") s12 nfa1;
val s13 = eclosure nfa1 s12b
val s13a = nodesOnFromMany (Char #"a") s13 nfa1;
val s13b = nodesOnFromMany (Char #"b") s13 nfa1;

fun showL f start xs sep finish =
  let fun help [] = ""
        | help [x] = f x
        | help (x::xs) = (f x)^sep^help xs
  in start ^ (help xs) ^ finish end

fun printL message xs = print (message^(showL Int.toString "[" xs "," "]"))

fun printLL message xs =
  let fun f xs = showL Int.toString "[" xs "," "]"
  in print (message^(showL f "" xs "; " "\n")) end

fun printLc state xs =
  let fun g (c,[]) = ""
        | g (c,xs) =  (showL Int.toString "[" state "," "]")^" --"^c^"--> "^(showL Int.toString "[" xs "," "]\n   ")
  in print (showL g "   " xs "" "\n") end


fun snd(x,y) = y;

fun nfa2dfa start edges =
  let val chars = nodup(sigma edges)
      val s0 = eclosure edges [start]
      val worklist = ref [s0]
      val work = ref []
      val old = ref []
      val newEdges = ref []
  in while (not (null (!worklist))) do
      ( work := hd(!worklist)
      ; old := (!work) :: (!old)
      ; worklist := tl(!worklist)
      ; let fun nextOn c = (Char.toString c,eclosure edges (nodesOnFromMany (Char c) (!work) edges))
            val possible = map nextOn chars
            fun add ((c,[])::xs) es = add xs es
              | add ((c,ss)::xs) es = add xs ((!work,c,ss)::es)
              | add [] es = es
            fun ok [] = false
              | ok xs = not(exists (fn ys => xs=ys) (!old)) andalso
                        not(exists (fn ys => xs=ys) (!worklist))
            val new = filter ok (map snd possible)
        in worklist := new @ (!worklist);
           newEdges := add possible (!newEdges)
        end
      );
     (s0,!old,!newEdges)
  end;



fun nfa2dfa2 start edges =
let val chars = nodup(sigma edges)
    val s0 = eclosure edges [start]
    fun help [] old newEdges = (s0,old,newEdges)
      | help (work::worklist) old newEdges =
          let val processed = work::old
              fun nextOn c = (Char.toString c
                             ,eclosure edges (nodesOnFromMany (Char c) work edges))
              val possible = map nextOn chars
              fun add ((c,[])::xs) es = add xs es
                | add ((c,ss)::xs) es = add xs ((work,c,ss)::es)
                | add [] es = es
              fun ok [] = false
                | ok xs = not(exists (fn ys => xs=ys) processed) andalso
                          not(exists (fn ys => xs=ys) worklist)
              val new = filter ok (map snd possible)
          in help (new @ worklist) processed
                  (add possible newEdges) end
in help [s0] [] [] end;






(* 
*)