{-# OPTIONS_GHC -fno-warn-tabs #-}
{-# OPTIONS_HADDOCK show-extensions #-}
{-# LANGUAGE TypeOperators #-}
{-|
Module      : Finite Automaton
Description : Finite Automaton
Copyright   : (c) Jorge Santiago Alvarez Cuadros, 2016
License     : GPL-3
Maintainer  : sanjorgek@ciencias.unam.mx
Stability   : experimental
Portability : portable

Finite Automaton is a stateful machine where all transition means that machine 
reads a symbol
-}
module Math.Model.Automaton.Finite
(
	-- * Deterministic
	-- ** Function 
	-- *** Recognizer
	Delta(..)
	,liftD
	-- ** Transducer
	,Lambda1(..)
	,liftL1
	,Lambda2(..)
	,liftL2
	-- ** Constructor
	,FiniteA(..)
	,Transductor(..)
	-- ** Function
	,checkString
	,translate
	-- * Not deterministic
	-- ** Function
	,DeltaN(..)
	,liftDN
	-- ** Constructor
	,FiniteAN(..)
	,checkStringN
) where
import Data.State
import Data.Sigma
import Data.Delta
import Data.List
import Data.Monoid
import Control.Monad
import qualified Data.Map.Lazy as Map
import qualified Data.Foldable as Fold

{-|
Transition function hava a State and a Symbol by domain to decide next state in 
machine
-}
type Delta a = (:->:) a Symbol ()

{-|
Lift a list of 3-tuples in a Delta

>>>let delta = liftD [(0,'0',0),(0,'1',1),(1,'0',1),(1,'1',0)]
-}
liftD::(Ord a) => [(a,Symbol,a)] -> Delta a
liftD ds = let
		(xs,ys,zs) = unzip3 ds
		f = map return 
		xys = zip (f xs) ys
		qzs = zip (f zs) (repeat ())
	in Map.fromList (zip xys qzs)

type Lambda1 a = (:*>:) a () Symbol

liftL1::(Ord a) => [(a, Symbol)] -> Lambda1 a
liftL1 ds = let
		(xs, ys) = unzip ds
		f = map return
		nds = zip (zip (f xs) (repeat ())) ys
	in Map.fromList nds

type Lambda2 a = (:*>:) a Symbol Symbol

liftL2::(Ord a) => [(a, Symbol, Symbol)] -> Lambda2 a
liftL2 ds = let
		(xs, ys, zs) = unzip3 ds
		f = map return
		nds = zip (zip (f xs) ys) zs
	in Map.fromList nds
 
{-|
Finite deterministic Automaton
-}
data FiniteA a = 
	-- |>>>let autFin = F delta [Q 0] (Q 0)
	F (Delta a) (Final a) (State a) deriving(Show, Eq)

{-|
Executes a automaton over a word

>>>checkString autFin "1010010101101010"
True
>>>checkString autFin "1010010101101010001010101010"
False
-}
checkString::(Ord a) => FiniteA a -> Wd -> Bool
checkString (F d qF s) ws = let
		q = checkString' d s ws
		f y = ((not.isError) y)&&(terminal qF y)
	in f q
	where
		checkString' _ q [] = q
		checkString' dt q (x:xs) = checkString' dt (nextD dt (q,x)) xs

data Transductor a = 
	Moore (Delta a) (Lambda1 a) (Final a) (State a) 
	|Mealy (Delta a) (Lambda2 a) (Final a) (State a) deriving(Show, Eq)

translate::(Ord a) => Transductor a -> Wd -> Wd
translate (Moore d l qF s) ws = let
		(q, w) = translate d l s ws []
	in w
	where
		translate _ _ QE xs ys = (QE, "Error: \nCadena:"++xs++"\nResp parcial: "++ys)
		translate _ _ q [] xs = (q, xs)
		translate dt lm q (y:ys) xs = translate dt lm (nextD dt (q,y)) ys (xs++[lm Map.! (q, ())])
translate (Mealy d l qF s) ws = let
		(q, w) = translate d l s ws []
	in ws
	where 
		translate _ _ QE xs ys = (QE, "Error: \nCadena:"++xs++"\nResp parcial: "++ys)
		translate _ _ q [] xs = (q, xs)
		translate dt lm q (x:xs) ys = translate dt lm (nextD dt (q, x)) xs (ys++[lm Map.! (q,x)])


type DeltaN a = (:>-:) a Symbol ()

{-|
Lift a list of 3-tuples in a non deterministic delta

>>>let deltaN = liftDN [(0,'0',[0]),(0,'1',[1]),(1,'0',[1]),(1,'1',[0])]
-}
liftDN::(Ord a) => [(a,Symbol,[a])] -> DeltaN a
liftDN ds = let
		(xs,ys,zs) = unzip3 ds
		f = map return
		xys = zip (f xs) ys
		qzs = zip (map f zs) (repeat ())
	in Map.fromList (zip xys qzs)

{-|
Finite non deterministic Automaton
-}
data FiniteAN a = 
	-- |>>>let autFinN = FN deltaN (terminal [Q 0]) (Q 0)
	FN (DeltaN a) (Final a) (State a) deriving(Show,Eq)

{-|
Executes a non-deterministic automaton over a word, maybe overload your pc 
-}
checkStringN::(Ord a) => FiniteAN a -> Wd -> Bool
checkStringN (FN dn qF s) ws = let
		qs = checkStringN' dn [s] ws
		f y = ((not.isError) y)&&(terminal qF y)
		g y = or (map f y)
	in g qs
	where
		check dt k = if Map.member k dt then dt Map.! k else ([QE], ())
		mDelta dt lq a = (nub.concat.(map fst)) (map (\q -> check dt (q,a)) lq)
		checkStringN' _ qs [] = qs
		checkStringN' dn qs (x:xs) = checkStringN' dn (mDelta dn qs x) xs