\begin{code}
module SetB (module SetB,module CollectionB) where
import CollectionB 
import MapG
import FranCore
import FM
import Identify
import List
import BVar

newtype Set a = Set (FiniteMap Ident a)
{-
  eltsSet,applySetOp,
  fromSet,toSet,keysSet,eltsSet,listToSet,setToList
-}
listToSet :: [(Ident,a)] -> Set a
listToSet = toSet . listToFM

setToList :: Set a -> [(Ident,a)]
setToList = fmToList . fromSet

keysSet :: Set a -> [Ident]
keysSet = keysFM . fromSet

eltsSet :: Set a -> [a]
eltsSet = eltsFM . fromSet

fromSet :: Set a -> FiniteMap Ident a
fromSet (Set a) = a

toSet :: FiniteMap Ident a -> Set a
toSet a = Set a

type SetB a = CollectionB SetOp Set a 

setBehavior :: SetB a -> Behavior [a]
setBehavior = lift1 eltsSet . behaviorC 

newSetB :: Set a -> Event (SetOp a) -> IO (SetB a)
newSetB s e = newCollectionB e s

emptySetB :: SetB a
emptySetB = CollectionB neverE (lift0 $ toSet emptyFM)

data SetOp a = InsertSet ! Ident ! a | DeleteSet ! Ident | ResetSet ! (Set a)

applySetOp :: SetOp a -> Set a -> Set a
applySetOp (InsertSet x a) s = toSet $ addToFM (fromSet s) x a
applySetOp (DeleteSet x) s = toSet $ delFromFM (fromSet s) x
applySetOp (ResetSet xs) _ = xs

instance Functor SetOp where
 fmap f (InsertSet x a) = InsertSet x (f a)
 fmap f (DeleteSet x) = DeleteSet x
 fmap f (ResetSet xs) = ResetSet (fmap f xs)

instance Functor Set where
 fmap f xs = toSet $ mapFM (\_ -> f) $ fromSet xs

instance Monad g => MapG Set a b g where
 mapG f xs = mapG f (eltsSet xs) >>= return . listToSet . zip (keysSet xs)

instance Monad g => MapG SetOp a b g where
 mapG f (InsertSet x a) = do a <- f a;return $ InsertSet x a
 mapG f (DeleteSet x) = return $ DeleteSet x
 mapG f (ResetSet xs) = mapG f xs >>= return . ResetSet 

instance CollectionOp SetOp Set where 
 applyOp = applySetOp
 resetOp = ResetSet

unionSetB :: Eq a => SetB a -> SetB a -> SetB a
unionSetB (c1 :: SetB a) c2 = mergeCB c1 c2 mergeE unionSets
  where
    unionSets :: Set a -> Set a -> Set a
    unionSets s1 s2 = 
       let l1 = setToList s1
	   l2 = map (\(k,e) -> (identify $ show k,e)) $ setToList s2

	   compare :: (Ident,a) -> (Ident,a) -> Bool
	   compare (i1,a1) (i2,a2) = a1 == a2
       in
	listToSet $ unionBy compare l1 l2 


    mergeE :: Event (SetOp a)
    mergeE = 
     mapMaybeE   (unOp True) 
	 (eventC c1 `snapshotE` pairB (behaviorC c1) (behaviorC c2)) 
	   
     .|. 
     mapMaybeE  (unOp False)
	(eventC c2 `snapshotE` pairB (behaviorC c2) (behaviorC c1)) 
	    

    unOp :: Bool -> (SetOp a,(Set a,Set a)) -> Maybe (SetOp a)
    unOp over (op@(InsertSet x a),(as,bs)) =
       if x `elemFM` fromSet as then 
         Nothing
       else
	 if not over && a `elem` (eltsSet bs) then 
            Nothing
         else 
	   Just $ if over then op
		  else InsertSet (identify $ show x) a

    unOp over (op@(DeleteSet x),(as,bs)) =
       if x `elemFM` fromSet as then
         if over then Just op
         else Just $ DeleteSet (identify $ show x)
       else
         Nothing

    unOp over (ResetSet rs,(_,bs)) = 
        Just $ ResetSet $ if over then unionSets rs bs else unionSets bs rs

intersectSetB :: Eq a => SetB a -> SetB a -> SetB a
intersectSetB c1 c2 = newSet 
  where
    newSet = mergeCB c1 c2 (mergeE c1 c2 newSet) intersectSets

    intersectSets :: Eq a => Set a -> Set a -> Set a
    intersectSets s1 s2 = 
       let l1 = setToList s1
	   l2 = map (\(k,e) -> (identify $ show k,e)) $ setToList s2

	   compare :: Eq a => (Ident,a) -> (Ident,a) -> Bool
	   compare (i1,a1) (i2,a2) = a1 == a2
       in
	listToSet $ intersectBy compare l1 l2 


    mergeE :: Eq a => SetB a -> SetB a -> SetB a -> Event (SetOp a)
    mergeE c1 c2 newSet = 
     mapMaybeE  (unOp True) (eventC c1 `snapshotE` 
		pairB (behaviorC newSet) (pairB (behaviorC c1) (behaviorC c2))) 
	    
     .|. 
     mapMaybeE  (unOp False) (eventC c2 `snapshotE` 
		pairB (behaviorC newSet) (pairB (behaviorC c2) (behaviorC c1))) 
	    

    unOp :: Eq a => Bool -> (SetOp a,(Set a,(Set a,Set a))) -> Maybe (SetOp a)
    unOp over (op@(InsertSet x a),(_,(as,bs))) =
       if x `elemFM` fromSet as then 
         Nothing
       else
	 if a `elem` eltsSet bs then
	   Just $ if over then op 
		  else InsertSet (identify $ show x) a
	 else 
	   Nothing


    unOp over (op@(DeleteSet x),(new,_)) = 
       let key = if over then x else (identify $ show x)
       in
       if key `elemFM` fromSet new then
         if over then Just op
         else Just $ DeleteSet key
       else
         Nothing

    unOp over (ResetSet rs,(_,(_,bs))) = 
        Just $ ResetSet $ if over then intersectSets rs bs 
			  else intersectSets bs rs

minusSetB :: Eq a => SetB a -> SetB a -> SetB a
minusSetB (c1 :: SetB a) c2 =  mergeCB c1 c2 mergeE minusSets
 where
    minusSets :: Set a -> Set a -> Set a
    minusSets s1 s2 = 
       let l1 = setToList s1
	   l2 = setToList s2

	   compare :: (Ident,a) -> (Ident,a) -> Bool
	   compare (i1,a1) (i2,a2) = a1 == a2
       in
	listToSet $ deleteFirstsBy compare l1 l2 


    mergeE :: Event (SetOp a)
    mergeE = 
     mapMaybeE (unOp True) 
	 (eventC c1 `snapshotE` (pairB (behaviorC c1) (behaviorC c2))) 
	     
     .|. 
     mapMaybeE (unOp False)
	(eventC c2 `snapshotE` (pairB (behaviorC c2) (behaviorC c1))) 
	     

    unOp :: Bool -> (SetOp a,(Set a,Set a)) -> Maybe (SetOp a)
    unOp this (op@(InsertSet x a),(as,bs)) =
      if not this then 
	case find (\(_,a') -> a == a') $ setToList bs of
            Nothing ->	Nothing
	    Just (k,_) -> Just $ DeleteSet k
      else
        if not (x `elemFM` fromSet as) && (a `elem` eltsSet bs) then
          Just op
        else
          Nothing

    unOp this (op@(DeleteSet x),(as,bs)) = 
        case lookupFM (fromSet as) x of
         Nothing -> Nothing
         Just a ->
          case find (\(_,a') -> a == a') $ setToList bs of
            Nothing | this -> Just op
            Just (k,a) | not this -> Just $ InsertSet k a 
	    _ -> Nothing

    unOp this (ResetSet rs,(_,bs)) = 
        Just $ ResetSet $ if this then minusSets rs bs 
			  else minusSets bs rs
\end{code}
