lazyset-0.1.0.0: Data/Set/Lazy.hs
{-|
Module : LazySet
Description : A truly lazy Set.
Copyright : (c) Carlos Freund, 2016
License : MIT
Maintainer : carlosfreund@gmail.com
Stability : experimental
A Set that can be created from lazy, ordered, infinite lists.
-}
module Data.Set.Lazy where
import Prelude hiding(lookup)
import qualified Data.List as List
import qualified Data.Set as NSet
import qualified Data.List.Ordered as OList
import Data.Maybe (isJust)
import Data.Ord
data LazySet a = LazySet [NSet.Set a]
deriving (Eq, Show)
-- * Query
-- | Checks if the value is a member of the 'LazySet'.
-- Performance: O(m)=log m
--Where m is the position of the element beeing searched for.
-- This only applies after the element has been fetched from the underlying list.
member :: Ord a => a -> LazySet a -> Bool
member e set = isJust $ lookup e set
-- | Searches for a value in a Set. If it can not be found returns 'Nothing' otherwhise
-- Returns 'Just a' if it can find it. The returned value will be the one from the set, not the one that was passed.
lookup :: Ord a => a -> LazySet a -> Maybe a
lookup e (LazySet sets) = findIn sets
where findIn [] = Nothing
findIn (set:rest) = case NSet.lookupLE e set of
Nothing -> Nothing
Just x -> if (x == e) then Just x else findIn rest
-- | Returns true if the 'LazSet' is empty.
null :: LazySet a -> Bool
null (LazySet (x:xs)) = True
null _ = False
-- | Returns the size of the set. Do not use this on infinite Sets.
size :: Ord a => LazySet a -> Int
size = length . toList
-- * Combine
-- | Splits the 'LazySet' into two parts.
-- The first containing all consecutive elements of the Set where the predicate applies.
-- The second contains the (infinite) rest.
spanAntitone :: Ord a => (a -> Bool) -> LazySet a -> (LazySet a, LazySet a)
spanAntitone pred (LazySet sets) = let
(lesser, (middle:higher)) = List.span (pred . NSet.findMax) sets
(middleLesser, middleHigher) = NSet.spanAntitone pred middle
in (LazySet (lesser++[middleLesser]), LazySet (middleHigher:higher))
-- | Union of two LazySets.
union :: Ord a => LazySet a -> LazySet a -> LazySet a
union s1 s2 = let
in fromList $ OList.union (toList s1) (toList s2)
-- * Build
-- | Create an 'empty' 'LazySet'.
empty :: Ord a => LazySet a
empty = fromList []
-- | Builds a 'LazySet' from an ascending ordered list.
-- If the list is not ordered an error is thrown.
fromAscList :: Ord a => [a] -> LazySet a
fromAscList = growFromAscList 2.0
-- | Like 'fromAscList' but with a custom growth-factor.
growFromAscList :: Ord a =>
Float -- ^ The factor by which the subtrees grow.
--Must be >= 1.0. A growth of 1.0 makes the 'LazySet' behave like a List.
-- The higher it is set, the more it behaves like a 'Data.Set'.
-- The downside of a higher growth-factor is that bigger batches are extracted from the source-list at once.
-> [a] -- ^ An ascending List
-> LazySet a
growFromAscList growth _ | growth < 1.0 = error "growth must be at least 1"
growFromAscList growth xs = LazySet (build 0 growth (checkDir xs))
where checkDir (a:b:s)| a > b = error "Elements must be ascending."
checkDir xs = xs
-- | Alias for 'fromAscList'.
fromList :: Ord a => [a] -> LazySet a
fromList = fromAscList
-- | Create a 'LazSet' from a descending list.
fromDescList :: Ord a => [a] -> LazySet (Down a)
fromDescList xs = fromAscList (map Down xs)
-- | Kind of internal.
build :: Ord a => Int -- ^ starting-depth
-> Float -- ^ growth-factor
-> [a] -- ^Ascending source-list
-> [NSet.Set a]
build _ _ [] = []
build level growth xs = let
(elementsForThisLevel, elementsFurtherDown) = List.splitAt (ceiling $ growth^level) xs
in (NSet.fromAscList elementsForThisLevel):(build (level + 1) growth elementsFurtherDown)
-- * Export
-- | List with all elements in order.
toList :: Ord a => LazySet a -> [a]
toList (LazySet sets) = concat $ map NSet.toAscList sets