content: 
  -- | This module defines the functions that can be used run the classical subset
  -- of QIO. That is, QIO computations that only use classical unitary operations.
  module QIO.QioClass where
  
  import Data.Maybe as Maybe
  import Data.Monoid as Monoid
  import Control.Monad.State
  import QIO.QioSyn
  import QIO.Heap
  
  -- | A classical unitary operation is defined as a function that will
  -- update the current classical state.
  newtype UnitaryC =  U {unU :: Int -> HeapMap -> HeapMap}
  
  -- | The classical unitary type forms a Monoid
  instance Monoid UnitaryC where
      mempty = U (\ fv bs -> bs)
      mappend (U f) (U g) = U (\ fv h -> g fv (f fv h))
  
  -- | A single qubit rotation can be converted into the classical unitary type, 
  -- if it is indeed classical (otherwise an error is thrown).
  uRotC :: Qbit -> Rotation -> UnitaryC
  uRotC x f | f==rnot    = U (\ _ h -> update h x (not (fromJust (h ? x))))
            | f==rid     = mempty
            | otherwise  = error "not classical"
  
  -- | A swap operation can be defined in the classical unitary type.
  uSwapC :: Qbit -> Qbit -> UnitaryC 
  uSwapC x y = U (\ _ h -> hswap h x y )
  
  -- | A conditional operation can be defined in the classical unitary type.
  uCondC :: Qbit -> (Bool -> UnitaryC) -> UnitaryC
  uCondC x br = U (\ fv h -> update (unU (br (fromJust (h ? x))) fv (forget h x)) x (fromJust (h ? x)))
  
  -- | A let operation can be defined in the classical unitary type.
  uLetC :: Bool -> (Qbit -> UnitaryC) -> UnitaryC
  uLetC b ux = U (\ fv h -> unU (ux (Qbit fv)) (fv+1) (update h (Qbit fv) b))
  
  -- | A unitary can be run by converting it into the classical unitary type.
  runUC :: U -> UnitaryC
  runUC UReturn = mempty
  runUC (Rot x r u) = uRotC x r `mappend` runUC u
  runUC (Swap x y u) = uSwapC x y `mappend` runUC u
  runUC (Cond x us u) = uCondC x (runUC.us) `mappend` runUC u
  runUC (Ulet b xu u) = uLetC b (runUC.xu) `mappend` runUC u
  
  -- | A classical state consists of the next free qubit reference, along with 
  -- a Heap that represents the overall state of the current qubits in scope.
  data StateC = StateC {fv :: Int, heap :: HeapMap}
  
  -- | An initial state is defined as an empty heap, with 0 set as the next 
  -- free qubit referece
  initialStateC :: StateC 
  initialStateC = StateC 0 initial
  
  -- | A QIO computation can be converted into a stateful computation, over
  -- a state of type "StateC".
  runQStateC :: QIO a -> State StateC a
  runQStateC (QReturn a) = return a
  runQStateC (MkQbit b xq) = do (StateC fv h) <- get 
                                put (StateC (fv+1) (update h (Qbit fv) b)) 
                                runQStateC (xq (Qbit fv))
  runQStateC (ApplyU u q) = do (StateC fv h) <- get
                               put (StateC fv (unU (runUC u) fv h))
                               runQStateC q 
  runQStateC (Meas x qs) = do (StateC _ h) <- get
                              runQStateC (qs (fromJust (h ? x)))
  
  -- | We can run a classical QIO computation by converting it into a stateful
  -- computation, and evaluating that using the initial state.
  runC :: QIO a -> a
  runC q = evalState (runQStateC q) initialStateC
  
            
  
  
  
  