### mindfa -- minimize a DFA

record dfa(Q,S,d,q0,F)          # a DFA

procedure main()

   x := getdfa()
   every 1 to 10 do
      showdfa("Reduced",minimize(showdfa("Original",x)))

end

## - getdfa() -- accept a dfa from input, return it
##
procedure getdfa()
local Q,S,d,q0,F
local q,a

   Q := readset("Enter states (1 character names): ")
   S := readset("Enter input alphabet: ")
   F := readset("Enter Final states (subset of states): ")
   writes("What is the start state? ")
   q0 := read()
   d := table()
   every q := !Q & a := !S do {
      writes("enter delta(",q,",",a,") = ")
      d[q||":"||a] := read()
      }
   return dfa(Q,S,d,q0,F)

end


## readset(s) - get a set
#
procedure readset(s)
local t1

   writes(s)
   t1 := []
   every put(t1,!cset(read()))  # the cset removes duplicates
   return t1

end

## showdfa(msg,D) -- show a dfa
#
procedure showdfa(msg,D)
local q,a

   every 1 to 3 do write()
   write(msg," Deterministic Finite Automaton is:")
   write()
   write("\t(Q,S,delta,q0,F)")
   write()
   write("where:")
   write()
   writeset("Q",D.Q)
   writeset("S",D.S)
   writeset("F",D.F)
   write("\tStart state is ",D.q0)
   write("\tDelta: ")
   every q := !D.Q do {
      every writes("\td(",q,",",a := !D.S,") = ",D.d[q||":"||a])
      write()
      }
   return D

end

## writeset(msg,s) -- display a set
#
procedure writeset(msg,s)
local tmp
   tmp := ""
   every tmp ||:= !s || ","
   write("\t",msg," = {",tmp[1:-1],"}")
   return
end

## minimize(D) -- minimize a dfa
#
global distab, dlists

procedure minimize(D)
local F,QF
local p,q,a,cs

   distab := table()
   dlists := table()
   F := D.F
   QF := diff(D.Q,D.F)
   every p := !F & q := !QF do
      distab[cset(p||q)] := "X"
   every ((p := !F & q := !F) |
          (p := !QF & q := !QF)) & p ~== q do
      if \distab[cset(D.d[p||":"||(a:=!D.S)]||D.d[q||":"||a])] then {
         distab[cset(p||q)] := "X"
         marklists(dlists[cset(p||q)])
         }
      else
         every a := !D.S do
            if D.d[p||":"||a] ~== D.d[q||":"||a] then {
               cs := cset(D.d[p||":"||a]||D.d[q||":"||a])
               if cs == cset(p||q) then next
               /dlists[cs] := []
               put(dlists[cs],cset(p||q))
               }

   return makemdfa(D,distab)

end

## marklists(l) -- recursively mark the pair of nodes
#                  on list l.
procedure marklists(l)
local e

   if /l then return
   every e := !l do {
      distab[e] := "X"
      marklists(dlists[e])
      }
   return

end

## makemdfa(D,DT) -- Use the table from the minimization
#                    to construct the minimal dfa
procedure makemdfa(D,DT)
local elist, etab, qset, tlist, echeck
local p, q, Delta, q0

   etab := table()              # table of new states
   qset := ''
   every p := !D.Q do {
      qset ++:= p
      plike := equiv(p,etab) | cset(p)
      every q := !diff(D.Q,qset) & p ~== q do
         if /distab[cset(p||q)] then {
            plike ++:= equiv(q,etab) | q
            }
      etab[plike] := plike
      }
   tlist := []
   elist := []
   Delta := table()
   q0 := equiv(D.q0,etab)       # start state of reduced machine
   put(tlist,q0)
   put(elist,q0)                # only worry about states reachable
                                #   from [q0]
   echeck := table()            #   keep track of states
   echeck[q0] := q0
   while q := get(tlist) do
      every a := !D.S do {
         Delta[q||":"||a] := equivdelta(q,a,D,etab)
         if /echeck[Delta[q||":"||a]] then {
            echeck[Delta[q||":"||a]] := Delta[q||":"||a]
            put(tlist,Delta[q||":"||a])
            put(elist,Delta[q||":"||a])
            }
         }

   return dfa(elist,D.S,Delta,q0,finalstates(D,elist))
end

## equiv(q,el) -- return the equivalence class in el containing q
#
procedure equiv(q,el)
   every p := !el do
      if p++q == p then return p
end

## equivdelta(p,a,D,el) -- apply delta to equiv. classes
#
procedure equivdelta(p,a,D,el)
local q, r
   q := !p               # any state in equiv. class p
   r := D.d[q||":"||a]   # find state in original dfa

   return equiv(r,el)    # return its equivalence class
end


## finalstates(D,el) -- build the set of final states
#
procedure finalstates(D,el)
local flist, p, q

   ftab := table()
   every p := !D.F do
      ftab[q := equiv(p,el)] := q
   flist := []
   every put(flist,(!sort(ftab))[1])
   return flist
end


## diff(l1,l2) -- return the difference of two sets
#
procedure diff(l1,l2)
local l,t1,t2

   t1 := ''
   every t1 ++:= !l1

   t2 := ''
   every t2 ++:= !l2

   l := []
   every put(l,!(t1--t2))
   if *l = 0 then fail
   return l
end
