{-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances #-}

module Data.Set.List (SetList(..)) where


import Data.Monoid
import qualified Data.List as List
import Prelude hiding (sum,concat,lookup,map,filter,foldr,foldr1,foldl,null,reverse,(++),minimum,maximum,all,elem,concatMap)
import Data.Collections
import Data.Collections.BaseInstances ()
import Data.Typeable

-- | View a list of as a 'Set' collection.
--
-- This allows to feed sequences into algorithms that require a Set without building a full-fledged Set.
-- Most of the time this will be used only when the parameter list is known to be very small, such that
-- conversion to a Set would be to costly.

--FIXME: Generalize to sequences.
newtype SetList s = SetList {fromSetList :: s}

instance (Eq s, Eq a, Foldable s a) => Eq (SetList s) where
    (SetList l1) == (SetList l2) = l1 == l2 || 
                                   (size l1 == size l2 && all (`elem` l1) l2)


#include "Typeable.h"
INSTANCE_TYPEABLE1(SetList,theTc,"Data.Set.List.SetList")

instance Show l => Show (SetList l) where
    show (SetList l) = "SetList " >< show l

instance Foldable (SetList [a]) a where
    foldr f z (SetList l) = foldr f z l
    null (SetList l) = null l

instance Eq a => Set (SetList [a]) a where
    haddock_candy = haddock_candy

instance Eq a => Monoid (SetList [a]) where
    mempty = empty
    mappend = union

instance Eq a => Unfoldable (SetList [a]) a where
    empty = SetList empty
    insert x (SetList l) = SetList $ if x `elem` l then l else insert x l
    
instance Eq a => Collection (SetList [a]) a where
    filter f (SetList l) = SetList $ filter f l

instance Eq a => Map (SetList [a]) a () where
    isSubmapBy f x y = isSubset x y && (f () () || null (intersection x y))
    insertWith _f k () = insert k
    unionWith _f = union
    intersectionWith _f = intersection
    mapWithKey _f = id
    differenceWith f s1 s2 = if f () () == Nothing then difference s1 s2 else s1
    lookup k l = if member k l then return () else fail "element not found"

    (SetList l1) `isSubset` (SetList l2) = all (`elem` l2) l1
    difference (SetList l1) (SetList l2) = SetList $ (List.\\) l1 l2
    delete k (SetList l) = SetList $ filter (not . (k ==)) l
    member k (SetList l) = List.elem k l
    union (SetList l1) (SetList l2) = SetList $ List.union l1 l2
    intersection (SetList l1) (SetList l2) = SetList $ List.intersect l1 l2
    alter f k l = let lk = lookup k l in
        case lk of
           Nothing -> case f lk of
                         Nothing -> l
                         Just _ -> insert k l
           Just _ -> case f lk of
                         Nothing -> delete k l
                         Just _ -> l