fixplate 0.1.4 → 0.1.5
raw patch · 19 files changed
+1549/−531 lines, 19 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.Generics.Fixplate.Hash: instance (Eq hash, ShowF f, Show hash) => ShowF (HashAnn hash f)
- Data.Generics.Fixplate.Hash.Class: class (Eq hash, Ord hash, Hashable hash) => HashValue hash where hashWord32 w = hashWord8 a . hashWord8 b . hashWord8 c . hashWord8 d where a = fromIntegral (255 .&. (w)) b = fromIntegral (255 .&. (shiftR w 8)) c = fromIntegral (255 .&. (shiftR w 16)) d = fromIntegral (255 .&. (shiftR w 24)) hashWord16 w = hashWord8 a . hashWord8 b where a = fromIntegral (255 .&. (w)) b = fromIntegral (255 .&. (shiftR w 8)) hashWord64 w = hashWord32 a . hashWord32 b where a = fromIntegral (4294967295 .&. (w)) b = fromIntegral (4294967295 .&. (shiftR w 32))
- Data.Generics.Fixplate.Hash.Class: class Hashable a where computeHash x = hashDigest x emptyHash
- Data.Generics.Fixplate.Hash.Class: computeHash :: (Hashable a, HashValue hash) => a -> hash
- Data.Generics.Fixplate.Hash.Class: emptyHash :: HashValue hash => hash
- Data.Generics.Fixplate.Hash.Class: hashBool :: HashValue hash => Bool -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashChar :: HashValue hash => Char -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashDigest :: (Hashable a, HashValue hash) => a -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashHash :: HashValue hash => hash -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashInt :: HashValue hash => Int -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashWord :: HashValue hash => Word -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashWord16 :: HashValue hash => Word16 -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashWord32 :: HashValue hash => Word32 -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashWord64 :: HashValue hash => Word64 -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: hashWord8 :: HashValue hash => Word8 -> hash -> hash
- Data.Generics.Fixplate.Hash.Class: instance (Hashable a, Hashable b) => Hashable (a, b)
- Data.Generics.Fixplate.Hash.Class: instance (Hashable a, Hashable b, Hashable c) => Hashable (a, b, c)
- Data.Generics.Fixplate.Hash.Class: instance (Hashable a, Hashable b, Hashable c, Hashable d) => Hashable (a, b, c, d)
- Data.Generics.Fixplate.Hash.Class: instance (Hashable a, Hashable b, Hashable c, Hashable d, Hashable e) => Hashable (a, b, c, d, e)
- Data.Generics.Fixplate.Hash.Class: instance Hashable Bool
- Data.Generics.Fixplate.Hash.Class: instance Hashable Char
- Data.Generics.Fixplate.Hash.Class: instance Hashable Int
- Data.Generics.Fixplate.Hash.Class: instance Hashable Word
- Data.Generics.Fixplate.Hash.Class: instance Hashable Word16
- Data.Generics.Fixplate.Hash.Class: instance Hashable Word32
- Data.Generics.Fixplate.Hash.Class: instance Hashable Word64
- Data.Generics.Fixplate.Hash.Class: instance Hashable Word8
- Data.Generics.Fixplate.Hash.Class: instance Hashable a => Hashable [a]
- Data.Generics.Fixplate.Hash.Class: showHex :: HashValue hash => hash -> String
- Data.Generics.Fixplate.Hash.FNV.FNV32: FNV32 :: Word32 -> FNV32
- Data.Generics.Fixplate.Hash.FNV.FNV32: instance Eq FNV32
- Data.Generics.Fixplate.Hash.FNV.FNV32: instance HashValue FNV32
- Data.Generics.Fixplate.Hash.FNV.FNV32: instance Hashable FNV32
- Data.Generics.Fixplate.Hash.FNV.FNV32: instance Ord FNV32
- Data.Generics.Fixplate.Hash.FNV.FNV32: instance Show FNV32
- Data.Generics.Fixplate.Hash.FNV.FNV32: newtype FNV32
- Data.Generics.Fixplate.Hash.FNV.FNV32: unFNV32 :: FNV32 -> Word32
- Data.Generics.Fixplate.Hash.FNV.FNV64: FNV64 :: Word64 -> FNV64
- Data.Generics.Fixplate.Hash.FNV.FNV64: instance Eq FNV64
- Data.Generics.Fixplate.Hash.FNV.FNV64: instance HashValue FNV64
- Data.Generics.Fixplate.Hash.FNV.FNV64: instance Hashable FNV64
- Data.Generics.Fixplate.Hash.FNV.FNV64: instance Ord FNV64
- Data.Generics.Fixplate.Hash.FNV.FNV64: instance Show FNV64
- Data.Generics.Fixplate.Hash.FNV.FNV64: newtype FNV64
- Data.Generics.Fixplate.Hash.FNV.FNV64: unFNV64 :: FNV64 -> Word64
- Data.Generics.Fixplate.Hash.Table: bag :: (Ord hash, Ord k) => (k -> hash) -> [k] -> HashTable hash k Int
- Data.Generics.Fixplate.Hash.Table: data HashTable hash k v
- Data.Generics.Fixplate.Hash.Table: empty :: (Ord hash, Ord k) => (k -> hash) -> HashTable hash k v
- Data.Generics.Fixplate.Hash.Table: fromList :: (Ord hash, Ord k) => (k -> hash) -> [(k, v)] -> HashTable hash k v
- Data.Generics.Fixplate.Hash.Table: getHashValue :: HashTable hash k v -> k -> hash
- Data.Generics.Fixplate.Hash.Table: insert :: (Ord hash, Ord k) => k -> v -> HashTable hash k v -> HashTable hash k v
- Data.Generics.Fixplate.Hash.Table: insertWith :: (Ord hash, Ord k) => (a -> v) -> (a -> v -> v) -> k -> a -> HashTable hash k v -> HashTable hash k v
- Data.Generics.Fixplate.Hash.Table: lookup :: (Ord hash, Ord k) => k -> HashTable hash k v -> Maybe v
- Data.Generics.Fixplate.Hash.Table: member :: (Ord hash, Ord k) => k -> HashTable hash k v -> Bool
- Data.Generics.Fixplate.Hash.Table: singleton :: (Ord hash, Ord k) => (k -> hash) -> k -> v -> HashTable hash k v
- Data.Generics.Fixplate.Hash.Table: toList :: Ord k => HashTable hash k v -> [(k, v)]
- Data.Generics.Fixplate.Hash.Table: unHashTable :: HashTable hash k v -> Map hash (Map k v)
- Data.Generics.Fixplate.Structure: liftAnn :: (f e -> g e) -> Ann f a e -> Ann g a e
- Data.Generics.Fixplate.Structure: restructure :: Functor f => NatTrafo f g -> Mu f -> Mu g
- Data.Generics.Fixplate.Structure: type NatTrafo f g = forall a. f a -> g a
+ Data.Generics.Fixplate.Attributes: synthRewrite' :: Traversable f => (f (Attr f a) -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a
+ Data.Generics.Fixplate.Attributes: synthTransform' :: Traversable f => (f (Attr f a) -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a
+ Data.Generics.Fixplate.Base: isAtom :: Foldable f => Mu f -> Bool
+ Data.Generics.Fixplate.Base: liftAnn :: (f e -> g e) -> Ann f a e -> Ann g a e
+ Data.Generics.Fixplate.Base: liftCoAnn :: (f e -> g e) -> CoAnn f a e -> CoAnn g a e
+ Data.Generics.Fixplate.Hash: HashValue :: hash -> (Char -> hash -> hash) -> (hash -> hash -> hash) -> HashValue hash
+ Data.Generics.Fixplate.Hash: _emptyHash :: HashValue hash -> hash
+ Data.Generics.Fixplate.Hash: _hashChar :: HashValue hash -> Char -> hash -> hash
+ Data.Generics.Fixplate.Hash: _hashHash :: HashValue hash -> hash -> hash -> hash
+ Data.Generics.Fixplate.Hash: data HashValue hash
+ Data.Generics.Fixplate.Hash: instance (ShowF f, Show hash) => ShowF (HashAnn hash f)
+ Data.Generics.Fixplate.Traversals: restructure :: Functor f => (f (Mu g) -> g (Mu g)) -> Mu f -> Mu g
+ Data.Generics.Fixplate.Traversals: restructureM :: (Traversable f, Monad m) => (f (Mu g) -> m (g (Mu g))) -> Mu f -> m (Mu g)
+ Data.Generics.Fixplate.Trie: bag :: (Functor f, Foldable f, OrdF f) => [Mu f] -> Trie f Int
+ Data.Generics.Fixplate.Trie: data Trie f v
+ Data.Generics.Fixplate.Trie: delete :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f a -> Trie f a
+ Data.Generics.Fixplate.Trie: difference :: (Functor f, Foldable f, OrdF f) => Trie f a -> Trie f b -> Trie f a
+ Data.Generics.Fixplate.Trie: differenceWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> Maybe a) -> Trie f a -> Trie f b -> Trie f a
+ Data.Generics.Fixplate.Trie: empty :: (Functor f, Foldable f, OrdF f) => Trie f a
+ Data.Generics.Fixplate.Trie: fromList :: (Traversable f, OrdF f) => [(Mu f, a)] -> Trie f a
+ Data.Generics.Fixplate.Trie: insert :: (Functor f, Foldable f, OrdF f) => Mu f -> a -> Trie f a -> Trie f a
+ Data.Generics.Fixplate.Trie: insertWith :: (Functor f, Foldable f, OrdF f) => (a -> b) -> (a -> b -> b) -> Mu f -> a -> Trie f b -> Trie f b
+ Data.Generics.Fixplate.Trie: instance EqF f => Eq (HoleF f)
+ Data.Generics.Fixplate.Trie: instance OrdF f => Ord (HoleF f)
+ Data.Generics.Fixplate.Trie: intersection :: (Functor f, Foldable f, OrdF f) => Trie f a -> Trie f b -> Trie f a
+ Data.Generics.Fixplate.Trie: intersectionWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> c) -> Trie f a -> Trie f b -> Trie f c
+ Data.Generics.Fixplate.Trie: lookup :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f a -> Maybe a
+ Data.Generics.Fixplate.Trie: singleton :: (Functor f, Foldable f, OrdF f) => Mu f -> a -> Trie f a
+ Data.Generics.Fixplate.Trie: toList :: (Traversable f, OrdF f) => Trie f a -> [(Mu f, a)]
+ Data.Generics.Fixplate.Trie: union :: (Functor f, Foldable f, OrdF f) => Trie f a -> Trie f a -> Trie f a
+ Data.Generics.Fixplate.Trie: unionWith :: (Functor f, Foldable f, OrdF f) => (a -> a -> a) -> Trie f a -> Trie f a -> Trie f a
+ Data.Generics.Fixplate.Trie: universeBag :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f Int
+ Data.Generics.Fixplate.Trie: update :: (Functor f, Foldable f, OrdF f) => (a -> Maybe a) -> Mu f -> Trie f a -> Trie f a
+ Data.Generics.Fixplate.Util.Hash.Class: class (Eq hash, Ord hash, Hashable hash) => HashValue hash where hashWord32 w = hashWord8 a . hashWord8 b . hashWord8 c . hashWord8 d where a = fromIntegral (255 .&. (w)) b = fromIntegral (255 .&. (shiftR w 8)) c = fromIntegral (255 .&. (shiftR w 16)) d = fromIntegral (255 .&. (shiftR w 24)) hashWord16 w = hashWord8 a . hashWord8 b where a = fromIntegral (255 .&. (w)) b = fromIntegral (255 .&. (shiftR w 8)) hashWord64 w = hashWord32 a . hashWord32 b where a = fromIntegral (4294967295 .&. (w)) b = fromIntegral (4294967295 .&. (shiftR w 32))
+ Data.Generics.Fixplate.Util.Hash.Class: class Hashable a where computeHash x = hashDigest x emptyHash
+ Data.Generics.Fixplate.Util.Hash.Class: computeHash :: (Hashable a, HashValue hash) => a -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: emptyHash :: HashValue hash => hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashBool :: HashValue hash => Bool -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashChar :: HashValue hash => Char -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashDigest :: (Hashable a, HashValue hash) => a -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashHash :: HashValue hash => hash -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashInt :: HashValue hash => Int -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashWord :: HashValue hash => Word -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashWord16 :: HashValue hash => Word16 -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashWord32 :: HashValue hash => Word32 -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashWord64 :: HashValue hash => Word64 -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: hashWord8 :: HashValue hash => Word8 -> hash -> hash
+ Data.Generics.Fixplate.Util.Hash.Class: instance (Hashable a, Hashable b) => Hashable (a, b)
+ Data.Generics.Fixplate.Util.Hash.Class: instance (Hashable a, Hashable b, Hashable c) => Hashable (a, b, c)
+ Data.Generics.Fixplate.Util.Hash.Class: instance (Hashable a, Hashable b, Hashable c, Hashable d) => Hashable (a, b, c, d)
+ Data.Generics.Fixplate.Util.Hash.Class: instance (Hashable a, Hashable b, Hashable c, Hashable d, Hashable e) => Hashable (a, b, c, d, e)
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Bool
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Char
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Int
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Word
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Word16
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Word32
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Word64
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable Word8
+ Data.Generics.Fixplate.Util.Hash.Class: instance Hashable a => Hashable [a]
+ Data.Generics.Fixplate.Util.Hash.Class: showHex :: HashValue hash => hash -> String
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: FNV32 :: Word32 -> FNV32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: instance Eq FNV32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: instance HashValue FNV32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: instance Hashable FNV32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: instance Ord FNV32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: instance Show FNV32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: newtype FNV32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32: unFNV32 :: FNV32 -> Word32
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: FNV64 :: Word64 -> FNV64
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: instance Eq FNV64
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: instance HashValue FNV64
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: instance Hashable FNV64
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: instance Ord FNV64
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: instance Show FNV64
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: newtype FNV64
+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64: unFNV64 :: FNV64 -> Word64
+ Data.Generics.Fixplate.Util.Hash.Table: Bucket :: {-# UNPACK #-} !Int -> !Map k (Leaf v) -> Bucket k v
+ Data.Generics.Fixplate.Util.Hash.Table: Leaf :: {-# UNPACK #-} !Int -> v -> Leaf v
+ Data.Generics.Fixplate.Util.Hash.Table: bag :: (Ord hash, Ord k) => (k -> hash) -> [k] -> HashTable hash k Int
+ Data.Generics.Fixplate.Util.Hash.Table: data Bucket k v
+ Data.Generics.Fixplate.Util.Hash.Table: data HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: data Leaf v
+ Data.Generics.Fixplate.Util.Hash.Table: delete :: (Ord hash, Ord k) => k -> HashTable hash k v -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: difference :: (Ord hash, Ord k) => HashTable hash k a -> HashTable hash k b -> HashTable hash k a
+ Data.Generics.Fixplate.Util.Hash.Table: differenceWith :: (Ord hash, Ord k) => (a -> b -> Maybe a) -> HashTable hash k a -> HashTable hash k b -> HashTable hash k a
+ Data.Generics.Fixplate.Util.Hash.Table: empty :: (Ord hash, Ord k) => (k -> hash) -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: fromList :: (Ord hash, Ord k) => (k -> hash) -> [(k, v)] -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: getHashValue :: HashTable hash k v -> k -> hash
+ Data.Generics.Fixplate.Util.Hash.Table: getUniqueIndex :: (Ord hash, Ord k) => (hash -> Int -> a) -> k -> HashTable hash k v -> Maybe a
+ Data.Generics.Fixplate.Util.Hash.Table: insert :: (Ord hash, Ord k) => k -> v -> HashTable hash k v -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: insertWith :: (Ord hash, Ord k) => (a -> v) -> (a -> v -> v) -> k -> a -> HashTable hash k v -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: intersection :: (Ord hash, Ord k) => HashTable hash k a -> HashTable hash k b -> HashTable hash k a
+ Data.Generics.Fixplate.Util.Hash.Table: intersectionWith :: (Ord hash, Ord k) => (a -> b -> c) -> HashTable hash k a -> HashTable hash k b -> HashTable hash k c
+ Data.Generics.Fixplate.Util.Hash.Table: intersectionsWith :: (Ord hash, Ord k) => (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: intersectionsWith' :: (Ord hash, Ord k) => (k -> hash) -> (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: keysWith :: Ord k => (k -> hash -> Int -> a) -> HashTable hash k v -> [a]
+ Data.Generics.Fixplate.Util.Hash.Table: lookup :: (Ord hash, Ord k) => k -> HashTable hash k v -> Maybe v
+ Data.Generics.Fixplate.Util.Hash.Table: mapWithUniqueIndices :: (Ord hash, Ord k) => (hash -> Int -> a -> b) -> HashTable hash k a -> HashTable hash k b
+ Data.Generics.Fixplate.Util.Hash.Table: member :: (Ord hash, Ord k) => k -> HashTable hash k v -> Bool
+ Data.Generics.Fixplate.Util.Hash.Table: null :: (Ord hash, Ord k) => HashTable hash k v -> Bool
+ Data.Generics.Fixplate.Util.Hash.Table: singleton :: (Ord hash, Ord k) => (k -> hash) -> k -> v -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: toList :: Ord k => HashTable hash k v -> [(k, v)]
+ Data.Generics.Fixplate.Util.Hash.Table: unHashTable :: HashTable hash k v -> Map hash (Bucket k v)
+ Data.Generics.Fixplate.Util.Hash.Table: union :: (Ord hash, Ord k) => HashTable hash k a -> HashTable hash k a -> HashTable hash k a
+ Data.Generics.Fixplate.Util.Hash.Table: unionWith :: (Ord hash, Ord k) => (v -> v -> v) -> HashTable hash k v -> HashTable hash k v -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: unionsWith :: (Ord hash, Ord k) => (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v
+ Data.Generics.Fixplate.Util.Hash.Table: unionsWith' :: (Ord hash, Ord k) => (k -> hash) -> (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v
- Data.Generics.Fixplate.Hash: hashNode :: (Foldable f, Functor f, ShowF f, HashValue hash) => f (HashMu hash f) -> HashMu hash f
+ Data.Generics.Fixplate.Hash: hashNode :: (Foldable f, Functor f, ShowF f) => HashValue hash -> f (HashMu hash f) -> HashMu hash f
- Data.Generics.Fixplate.Hash: hashNodeWith :: (Foldable f, Functor f, HashValue hash) => (f Hole -> hash -> hash) -> f (HashMu hash f) -> HashMu hash f
+ Data.Generics.Fixplate.Hash: hashNodeWith :: (Foldable f, Functor f) => HashValue hash -> (f Hole -> hash -> hash) -> f (HashMu hash f) -> HashMu hash f
- Data.Generics.Fixplate.Hash: hashTree :: (Foldable f, Functor f, ShowF f, HashValue hash) => Mu f -> HashMu hash f
+ Data.Generics.Fixplate.Hash: hashTree :: (Foldable f, Functor f, ShowF f) => HashValue hash -> Mu f -> HashMu hash f
- Data.Generics.Fixplate.Hash: hashTreeWith :: (Foldable f, Functor f, HashValue hash) => (f Hole -> hash -> hash) -> Mu f -> HashMu hash f
+ Data.Generics.Fixplate.Hash: hashTreeWith :: (Foldable f, Functor f) => HashValue hash -> (f Hole -> hash -> hash) -> Mu f -> HashMu hash f
Files
- Data/Generics/Fixplate.hs +28/−11
- Data/Generics/Fixplate/Attributes.hs +12/−6
- Data/Generics/Fixplate/Base.hs +24/−10
- Data/Generics/Fixplate/Hash.hs +104/−35
- Data/Generics/Fixplate/Hash/Class.hs +0/−99
- Data/Generics/Fixplate/Hash/FNV/FNV32.hs +0/−94
- Data/Generics/Fixplate/Hash/FNV/FNV64.hs +0/−97
- Data/Generics/Fixplate/Hash/Table.hs +0/−109
- Data/Generics/Fixplate/Misc.hs +20/−0
- Data/Generics/Fixplate/Morphisms.hs +1/−1
- Data/Generics/Fixplate/Structure.hs +0/−53
- Data/Generics/Fixplate/Tests.hs +10/−1
- Data/Generics/Fixplate/Traversals.hs +40/−1
- Data/Generics/Fixplate/Trie.hs +477/−0
- Data/Generics/Fixplate/Util/Hash/Class.hs +99/−0
- Data/Generics/Fixplate/Util/Hash/FNV/FNV32.hs +94/−0
- Data/Generics/Fixplate/Util/Hash/FNV/FNV64.hs +97/−0
- Data/Generics/Fixplate/Util/Hash/Table.hs +530/−0
- fixplate.cabal +13/−14
Data/Generics/Fixplate.hs view
@@ -2,7 +2,7 @@ -- | This library provides Uniplate-style generic traversals and other recursion schemes for fixed-point types. -- There are many advantages of using fixed-point types instead of explicit recursion: -- --- * we can add attributes to the nodes of an existing tree; +-- * we can easily add attributes to the nodes of an existing tree; -- -- * there is no need for a custom type class, we can build everything on the top of -- 'Functor', 'Foldable' and 'Traversable', for which GHC can derive the instances for us; @@ -12,7 +12,7 @@ -- * some operations can retain the structure of the tree, instead flattening -- it into a list; -- --- * it is quite straightforward to provide a generic zipper, generic tree drawing, generic hashing, etc... +-- * it is relatively straightforward to provide generic implementations of the zipper, tries, tree drawing, hashing, etc... -- -- The main disadvantage is that it does not work well for -- mutually recursive data types, and that pattern matching becomes @@ -21,7 +21,7 @@ -- Consider as an example the following simple expression language, -- encoded by a recursive algebraic data type: -- --- > Expr +-- > data Expr -- > = Kst Int -- > | Var String -- > | Add Expr Expr @@ -29,7 +29,7 @@ -- -- We can open up the recursion, and obtain a /functor/ instead: -- --- > Expr1 e +-- > data Expr1 e -- > = Kst Int -- > | Var String -- > | Add e e @@ -44,24 +44,42 @@ -- The functions in this library work on types like that: 'Mu'@ f@, -- where @f@ is a functor, and sometimes explicitely on 'Attr'@ f a@. -- --- This module re-exports most, but not all of the functionality present in the library. +-- The organization of the modules (excluding Util.\*) is the following: -- --- The library should be fully Haskell98 compatible, with the exception --- of the module "Data.Generics.Fixplate.Structure", which needs --- the @Rank2Types@ extension. For compatibility, the functionality --- of this module is at the moment only provided when compiled with GHC or Hugs. +-- * "Data.Generics.Fixplate.Base" - core types and type classes -- +-- * "Data.Generics.Fixplate.Traversals" - Uniplate-style traversals +-- +-- * "Data.Generics.Fixplate.Morphisms" - recursion schemes +-- +-- * "Data.Generics.Fixplate.Attributes" - annotated trees +-- +-- * "Data.Generics.Fixplate.Open" - functions operating on functors +-- +-- * "Data.Generics.Fixplate.Zipper" - generic zipper +-- +-- * "Data.Generics.Fixplate.Draw" - generic tree drawing (both ASCII and graphviz) +-- +-- * "Data.Generics.Fixplate.Trie" - generic generalized tries +-- +-- * "Data.Generics.Fixplate.Hash" - annotating trees with their hashes +-- +-- This module re-exports the most common functionality present in the library +-- (but not for example the zipper, tries, hashing). +-- +-- +-- The library itself should be fully Haskell98 compatible; no language extensions are used. -- Note: to obtain 'Eq', 'Ord', 'Show', 'Read' and other instances for -- fixed point types like @Mu Expr1@, consult the documentation of the -- 'EqF' type class (cf. the related 'OrdF', 'ShowF' and 'ReadF' classes) -- + module Data.Generics.Fixplate ( module Data.Generics.Fixplate.Base , module Data.Generics.Fixplate.Traversals , module Data.Generics.Fixplate.Morphisms , module Data.Generics.Fixplate.Attributes -- , module Data.Generics.Fixplate.Zipper - , module Data.Generics.Fixplate.Structure , module Data.Generics.Fixplate.Draw , Functor(..) , Foldable(..) , Traversable(..) ) @@ -74,7 +92,6 @@ import Data.Generics.Fixplate.Morphisms import Data.Generics.Fixplate.Attributes -- import Data.Generics.Fixplate.Zipper -import Data.Generics.Fixplate.Structure import Data.Generics.Fixplate.Draw import Data.Foldable
Data/Generics/Fixplate/Attributes.hs view
@@ -28,8 +28,8 @@ , synthAccumL_ , synthAccumR_ , enumerateNodes , enumerateNodes_ -- * Resynthetising transformations - , synthTransform - , synthRewrite + , synthTransform , synthTransform' + , synthRewrite , synthRewrite' -- * Stacking attributes , annZip , annZipWith , annZip3 , annZipWith3 @@ -345,8 +345,11 @@ -- | Bottom-up transformations which automatically resynthetise attributes -- in case of changes. synthTransform :: Traversable f => (f a -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a -synthTransform calc h0 = snd . go False where - synth x = Fix $ Ann (calc $ fmap attribute x) x +synthTransform calc = synthTransform' (calc . fmap attribute) + +synthTransform' :: Traversable f => (f (Attr f a) -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a +synthTransform' calc h0 = snd . go False where + synth x = Fix $ Ann (calc x) x hsynth x = case h0 (synth x) of Nothing -> Nothing Just y -> Just (synth y) @@ -363,9 +366,12 @@ -- | Bottom-up transformations to normal form (applying transformation exhaustively) -- which automatically resynthetise attributes in case of changes. synthRewrite :: Traversable f => (f a -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a -synthRewrite calc h0 = rewrite where +synthRewrite calc = synthRewrite' (calc . fmap attribute) + +synthRewrite' :: Traversable f => (f (Attr f a) -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a +synthRewrite' calc h0 = rewrite where rewrite = snd . go False - synth x = Fix $ Ann (calc $ fmap attribute x) x + synth x = Fix $ Ann (calc x) x hsynth x = case h0 (synth x) of Nothing -> Nothing Just y -> Just (synth y)
Data/Generics/Fixplate/Base.hs view
@@ -22,16 +22,9 @@ -- | The fixed-point type. newtype Mu f = Fix { unFix :: f (Mu f) } --------------------------------------------------------------------------------- --- * Attributes - --- | The attribute of the root node. -attribute :: Attr f a -> a -attribute = attr . unFix - --- | A function forgetting all the attributes from an annotated tree. -forget :: Functor f => Attr f a -> Mu f -forget = Fix . fmap forget . unAnn . unFix +-- | We call a tree \"atomic\" if it has no subtrees. +isAtom :: Foldable f => Mu f -> Bool +isAtom = null . toList . unFix -------------------------------------------------------------------------------- -- * Annotations @@ -46,6 +39,10 @@ -- | Annotated fixed-point type. Equivalent to @CoFree f a@ type Attr f a = Mu (Ann f a) +-- | Lifting natural transformations to annotations. +liftAnn :: (f e -> g e) -> Ann f a e -> Ann g a e +liftAnn trafo (Ann a x) = Ann a (trafo x) + -------------------------------------------------------------------------------- -- * Co-annotations @@ -57,6 +54,23 @@ -- | Categorical dual of 'Attr'. Equivalent to @Free f a@ type CoAttr f a = Mu (CoAnn f a) + +-- | Lifting natural transformations to annotations. +liftCoAnn :: (f e -> g e) -> CoAnn f a e -> CoAnn g a e +liftCoAnn trafo x = case x of + Pure x -> Pure x + CoAnn t -> CoAnn (trafo t) + +-------------------------------------------------------------------------------- +-- * Annotated trees + +-- | The attribute of the root node. +attribute :: Attr f a -> a +attribute = attr . unFix + +-- | A function forgetting all the attributes from an annotated tree. +forget :: Functor f => Attr f a -> Mu f +forget = Fix . fmap forget . unAnn . unFix -------------------------------------------------------------------------------- -- * Holes
Data/Generics/Fixplate/Hash.hs view
@@ -7,12 +7,12 @@ -- children directly; this way, you can also incrementally build up a hashed tree. -- module Data.Generics.Fixplate.Hash - ( -- * Type classes for different hash functions - module Data.Generics.Fixplate.Hash.Class - -- * Hashed tree type - , HashAnn(..) , getHash , unHashAnn + ( -- * Hashed tree type + HashAnn(..) , getHash , unHashAnn , HashMu , topHash , forgetHash + -- * Interface to the user's hash functions + , HashValue(..) -- * Hashing tres , hashTree , hashTreeWith , hashNode , hashNodeWith @@ -20,8 +20,10 @@ -------------------------------------------------------------------------------- -import Data.Generics.Fixplate.Hash.Class +-- import Data.Generics.Fixplate.Hash.Class +import Prelude as Prelude + import Control.Monad ( liftM ) import Control.Applicative ( (<$>) ) @@ -57,24 +59,6 @@ -------------------------------------------------------------------------------- -{- --- | This is a newtype so that we can define the 'Hashable' instance in Haskell98. --- With the @FlexibleInstances@ extensions, this is not necessary. -newtype HashableHashMu hash f = HHMu { unHHMu :: HashMu hash f } deriving (Eq,Ord,Show) - --- | This is a rather tricky instance, in the sense that --- --- > computeHash tree /= topHash tree --- --- Actually, the above does not even type-checks... --- But in practice, we would use the same type for both sides, so be careful. --- -instance HashValue hash => Hashable (HashableHashMu hash f) where - hashDigest t = hashDigest (topHash (unHHMu t)) --} - --------------------------------------------------------------------------------- - instance Functor f => Functor (HashAnn hash f) where fmap f (HashAnn attr t) = HashAnn attr (fmap f t) @@ -97,7 +81,7 @@ GT -> GT EQ -> compareF x1 x2 -instance (Eq hash, ShowF f, Show hash) => ShowF (HashAnn hash f) where +instance (ShowF f, Show hash) => ShowF (HashAnn hash f) where showsPrecF d (HashAnn hash x) = showParen (d>app_prec) $ showString "HashAnn " . showsPrec (app_prec+1) hash @@ -116,10 +100,15 @@ data Void = Void ; instance Show Void where show _ = "_" -{-# INLINE showDigest #-} +{- showDigest :: (Functor f, ShowF f, HashValue hash) => f a -> hash -> hash showDigest t = hashDigest $ showF (fmap (const Void) t) +-} +{-# INLINE showDigest #-} +showDigest :: (Functor f, ShowF f) => HashValue hash -> f a -> hash -> hash +showDigest hashv t = _hashString hashv $ showF (fmap (const Void) t) + -------------------------------------------------------------------------------- -- | This function uses the 'ShowF' instance to compute @@ -128,23 +117,103 @@ -- -- However, you can also supply your own hash implementation -- (which can be more efficient, for example), if you use 'hashTreeWith' instead. -hashTree :: (Foldable f, Functor f, ShowF f, HashValue hash) => Mu f -> HashMu hash f -hashTree = hashTreeWith showDigest +hashTree :: (Foldable f, Functor f, ShowF f) => HashValue hash -> Mu f -> HashMu hash f +hashTree hashv = hashTreeWith hashv (showDigest hashv) -hashTreeWith :: (Foldable f, Functor f, HashValue hash) => (f Hole -> hash -> hash) -> Mu f -> HashMu hash f -hashTreeWith user = go where - go (Fix x) = hashNodeWith user (fmap go x) +hashTreeWith :: (Foldable f, Functor f) => HashValue hash -> (f Hole -> hash -> hash) -> Mu f -> HashMu hash f +hashTreeWith hashv user = go where + go (Fix x) = worker (fmap go x) + worker = hashNodeWith hashv user -------------------------------------------------------------------------------- +-- | A concrete hash implementation. We don't use type classes since +-- +-- * a hash type class does not belong to this library; +-- +-- * we don't want to restrict the user's design space +-- +-- Thus we simulate type classes with record types. +-- +data HashValue hash = HashValue + { _emptyHash :: hash -- ^ the hash of an empty byte sequence +{- + , _hashWord8 :: Word8 -> hash -> hash -- ^ digest a byte + , _hashWord16 :: Word16 -> hash -> hash -- ^ digest two bytes + , _hashWord32 :: Word32 -> hash -> hash -- ^ digest four bytes + , _hashWord64 :: Word64 -> hash -> hash -- ^ digest eight bytes +-} + , _hashChar :: Char -> hash -> hash -- ^ digest a (unicode) character + , _hashHash :: hash -> hash -> hash -- ^ digest a hash value + } + +{-# INLINE _hashString #-} +_hashString :: HashValue hash -> String -> hash -> hash +_hashString hashv xs e = Prelude.foldr f e xs where + f = _hashChar hashv + +{-# INLINE _computeHash #-} +_computeHash :: HashValue hash -> [hash] -> hash +_computeHash hashv hs = Prelude.foldr f e hs where + e = _emptyHash hashv + f = _hashHash hashv + +{- + +-- | A minimal hash implementation. For efficiency reasons, we make a distinction between +-- this and 'HashValue' (for example if a hash function can readily digest 32 bit words, +-- it will be probably faster than if we feed bytes to it). +-- +-- The function 'makeHashValue' can be used to convert between the two. +data ByteHashValue = ByteHashValue + { _minEmptyHash :: hash -- ^ the hash of an empty byte sequence + , _minHashWord8 :: Word8 -> hash -> hash -- ^ digest a byte + , _minHashBytes :: hash -> [Word8] -- ^ convert a hash value to a sequence of bytes + } + +makeHashValue :: ByteHashValue hash -> HashValue hash +makeHashValue (ByteHashable empty hashWord8 hashBytes) = + HashValue + { _emptyHash = empty +{- + , _hashWord8 = hashWord8 + , _hashWord16 = hashWord16 + , _hashWord32 = hashWord32 + , _hashWord64 = hashWord64 +-} + , _hashChar c = hashChar c + , _hashHash h = foldr (.) id (map hashWord8 $ hashBytes h) + } + where + hashWord32 w = hashWord8 a . hashWord8 b . hashWord8 c . hashWord8 d where + a = fromIntegral (255 .&. ( w )) + b = fromIntegral (255 .&. (shiftR w 8)) + c = fromIntegral (255 .&. (shiftR w 16)) + d = fromIntegral (255 .&. (shiftR w 24)) + hashWord16 w = hashWord8 a . hashWord8 b where + a = fromIntegral (255 .&. ( w )) + b = fromIntegral (255 .&. (shiftR w 8)) + hashWord64 w = hashWord32 a . hashWord32 b where + a = fromIntegral (0xffffffff .&. ( w )) + b = fromIntegral (0xffffffff .&. (shiftR w 32)) + + -- We only use the lowest 16 bits here. This is questionable, + -- but typical use case is ASCII, 16 bits cover a big part of Unicode, and for byte based + -- hashes it is twice as fast as the more correct 32 bit version would be. + hashChar c = hashWord16 (fromIntegral $ ord c) + +-} + +-------------------------------------------------------------------------------- + -- | Build a hashed node from the children. -hashNode :: (Foldable f, Functor f, ShowF f, HashValue hash) => f (HashMu hash f) -> HashMu hash f -hashNode = hashNodeWith showDigest +hashNode :: (Foldable f, Functor f, ShowF f) => HashValue hash -> f (HashMu hash f) -> HashMu hash f +hashNode hashv = hashNodeWith hashv (showDigest hashv) -hashNodeWith :: (Foldable f, Functor f, HashValue hash) => (f Hole -> hash -> hash) -> f (HashMu hash f) -> HashMu hash f -hashNodeWith user x = Fix (HashAnn h x) where +hashNodeWith :: (Foldable f, Functor f) => HashValue hash -> (f Hole -> hash -> hash) -> f (HashMu hash f) -> HashMu hash f +hashNodeWith hashv user x = Fix (HashAnn h x) where h = user (fmap (const Hole) x) h0 - h0 = computeHash $ toList $ fmap (getHash . unFix) x + h0 = _computeHash hashv $ toList $ fmap (getHash . unFix) x -- h0 = foldl' (flip hashHash) emptyHash $ toList $ fmap (getHash . unFix) x --------------------------------------------------------------------------------
− Data/Generics/Fixplate/Hash/Class.hs
@@ -1,99 +0,0 @@- --- | Haskell98 polymorphic Hash interface -module Data.Generics.Fixplate.Hash.Class where - --------------------------------------------------------------------------------- - -import Data.Char -import Data.Word -import Data.Int -import Data.Bits -import Data.List - --------------------------------------------------------------------------------- - --- | A type class for hashes. --- Minimal complete definition: 'emptyHash', 'hashWord8', 'hashHash' and 'showHex'. -class (Eq hash, Ord hash, Hashable hash) => HashValue hash where - - hashWord8 :: Word8 -> hash -> hash - hashWord16 :: Word16 -> hash -> hash - hashWord32 :: Word32 -> hash -> hash - hashWord64 :: Word64 -> hash -> hash - - emptyHash :: hash - hashHash :: hash -> hash -> hash - showHex :: hash -> String - - hashWord32 w = hashWord8 a . hashWord8 b . hashWord8 c . hashWord8 d where - a = fromIntegral (255 .&. ( w )) - b = fromIntegral (255 .&. (shiftR w 8)) - c = fromIntegral (255 .&. (shiftR w 16)) - d = fromIntegral (255 .&. (shiftR w 24)) - - hashWord16 w = hashWord8 a . hashWord8 b where - a = fromIntegral (255 .&. ( w )) - b = fromIntegral (255 .&. (shiftR w 8)) - - hashWord64 w = hashWord32 a . hashWord32 b where - a = fromIntegral (0xffffffff .&. ( w )) - b = fromIntegral (0xffffffff .&. (shiftR w 32)) - --------------------------------------------------------------------------------- - --- | A type class of hashable objects. An instance has to compute the hash for --- /any/ hash function, using the \"base\" types (eg. Word32). --- --- Minimal complete definition: 'hashDigest'. The default for 'computeHash' is --- --- > computeHash x = hashDigest x emptyHash --- -class Hashable a where - hashDigest :: HashValue hash => a -> hash -> hash - computeHash :: HashValue hash => a -> hash - - computeHash x = hashDigest x emptyHash - --------------------------------------------------------------------------------- - -instance Hashable Word8 where hashDigest = hashWord8 -instance Hashable Word16 where hashDigest = hashWord16 -instance Hashable Word32 where hashDigest = hashWord32 -instance Hashable Word64 where hashDigest = hashWord64 - -instance Hashable Int where hashDigest = hashInt -instance Hashable Word where hashDigest = hashWord -instance Hashable Bool where hashDigest = hashBool -instance Hashable Char where hashDigest = hashChar - --------------------------------------------------------------------------------- - -instance Hashable a => Hashable [a] where - hashDigest xs h = foldl' (flip hashDigest) h xs - -instance (Hashable a, Hashable b) => Hashable (a,b) where - hashDigest (x,y) = hashDigest y . hashDigest x - -instance (Hashable a, Hashable b, Hashable c) => Hashable (a,b,c) where - hashDigest (x,y,z) = hashDigest z . hashDigest y . hashDigest x - -instance (Hashable a, Hashable b, Hashable c, Hashable d) => Hashable (a,b,c,d) where - hashDigest (x,y,z,w) = hashDigest w . hashDigest z . hashDigest y . hashDigest x - -instance (Hashable a, Hashable b, Hashable c, Hashable d, Hashable e) => Hashable (a,b,c,d,e) where - hashDigest (x,y,z,w,u) = hashDigest u . hashDigest w . hashDigest z . hashDigest y . hashDigest x - --------------------------------------------------------------------------------- - -hashInt :: HashValue hash => Int -> hash -> hash -hashWord :: HashValue hash => Word -> hash -> hash -hashBool :: HashValue hash => Bool -> hash -> hash -hashChar :: HashValue hash => Char -> hash -> hash - -hashInt k = hashWord64 (fromIntegral k) -hashWord k = hashWord64 (fromIntegral k) -hashBool b = hashWord8 (if b then 255 else 0) -hashChar c = hashWord16 (fromIntegral (ord c)) - --------------------------------------------------------------------------------- -
− Data/Generics/Fixplate/Hash/FNV/FNV32.hs
@@ -1,94 +0,0 @@---- | 32-bit FNV-1a (Fowler-Noll-Vo) hash--{-# LANGUAGE CPP #-}-module Data.Generics.Fixplate.Hash.FNV.FNV32 - ( FNV32(..) - , unFNV32 - ) - where------------------------------------------------------------------------------------import Data.Char-import Data.Word-import Data.Bits--- import Data.Int--- import Data.List--import Data.Generics.Fixplate.Hash.Class ------------------------------------------------------------------------------------newtype FNV32 = FNV32 Word32 deriving (Eq,Ord,Show)--unFNV32 :: FNV32 -> Word32-unFNV32 (FNV32 x) = x--instance Hashable FNV32 where - hashDigest (FNV32 w) = hashDigest w--instance HashValue FNV32 where- emptyHash = FNV32 fnv32_offset - hashHash (FNV32 w) = hashWord32 w- showHex (FNV32 w) = showHex32 w- hashWord8 x (FNV32 w) = FNV32 (fnv32_octet x w)- hashWord16 x (FNV32 w) = FNV32 (fnv32_word16 x w)- hashWord32 x (FNV32 w) = FNV32 (fnv32_word32 x w)- hashWord64 x (FNV32 w) = FNV32 (fnv32_word64 x w)------------------------------------------------------------------------------------showHex32 :: Word32 -> String-showHex32 h = reverse $ worker 8 h where- worker :: Int -> Word32 -> String- worker 0 0 = []- worker 0 _ = error "Hash/FNV32/showHex: shouldn't happen"- worker i w = hexdigit (w .&. 15) : worker (i-1) (shiftR w 4) - hexdigit :: Word32 -> Char- hexdigit n- | k>=0 && k<=9 = chr (k+48)- | otherwise = chr (k+55)- where k = fromIntegral n------------------------------------------------------------------------------------- FNV-1a hash--fnv32_prime, fnv32_offset :: Word32--fnv32_prime = 16777619 -fnv32_offset = 2166136261 --fnv32_octet :: Word8 -> Word32 -> Word32-fnv32_octet octet old = fnv32_prime * (old `xor` fromIntegral octet)------------------------------------------------------------------------------------- 32 bit--fnv32_word32 :: Word32 -> Word32 -> Word32-fnv32_word32 w = fnv32_octet a . fnv32_octet b . fnv32_octet c . fnv32_octet d where- a = fromIntegral (255 .&. ( w ))- b = fromIntegral (255 .&. (shiftR w 8))- c = fromIntegral (255 .&. (shiftR w 16))- d = fromIntegral (255 .&. (shiftR w 24))--{--fnv32_word24 :: Word32 -> Word32 -> Word32-fnv32_word24 w = fnv32_octet a . fnv32_octet b . fnv32_octet c where- a = fromIntegral (255 .&. ( w ))- b = fromIntegral (255 .&. (shiftR w 8))- c = fromIntegral (255 .&. (shiftR w 16))--}--fnv32_word16 :: Word16 -> Word32 -> Word32-fnv32_word16 w = fnv32_octet a . fnv32_octet b where- a = fromIntegral (255 .&. ( w ))- b = fromIntegral (255 .&. (shiftR w 8))--fnv32_word64 :: Word64 -> Word32 -> Word32-fnv32_word64 w = fnv32_word32 a . fnv32_word32 b where- a = fromIntegral (0xffffffff .&. ( w ))- b = fromIntegral (0xffffffff .&. (shiftR w 32))-----------------------------------------------------------------------------------
− Data/Generics/Fixplate/Hash/FNV/FNV64.hs
@@ -1,97 +0,0 @@---- | 64-bit FNV-1a (Fowler-Noll-Vo) hash--{-# LANGUAGE CPP #-}-module Data.Generics.Fixplate.Hash.FNV.FNV64 - ( FNV64(..) - , unFNV64- ) - where------------------------------------------------------------------------------------import Data.Char-import Data.Word-import Data.Bits--- import Data.Int--- import Data.List--import Data.Generics.Fixplate.Hash.Class------------------------------------------------------------------------------------instance Hashable FNV64 where - hashDigest (FNV64 w) = hashDigest w--instance HashValue FNV64 where- emptyHash = FNV64 fnv64_offset - hashHash (FNV64 w) = hashWord64 w- showHex (FNV64 w) = showHex64 w- hashWord8 x (FNV64 w) = FNV64 (fnv64_octet x w)- hashWord16 x (FNV64 w) = FNV64 (fnv64_word16 x w)- hashWord32 x (FNV64 w) = FNV64 (fnv64_word32 x w)- hashWord64 x (FNV64 w) = FNV64 (fnv64_word64 x w)- -----------------------------------------------------------------------------------newtype FNV64 = FNV64 Word64 deriving (Eq,Ord,Show)--unFNV64 :: FNV64 -> Word64-unFNV64 (FNV64 x) = x------------------------------------------------------------------------------------showHex64 :: Word64 -> String-showHex64 h = reverse $ worker 16 h where- worker :: Int -> Word64 -> String- worker 0 0 = []- worker 0 _ = error "Hash/FNV64/showHex: shouldn't happen"- worker i w = hexdigit (w .&. 15) : worker (i-1) (shiftR w 4) - hexdigit :: Word64 -> Char- hexdigit n- | k>=0 && k<=9 = chr (k+48)- | otherwise = chr (k+55)- where k = fromIntegral n------------------------------------------------------------------------------------- FNV-1a hash--fnv64_prime, fnv64_offset :: Word64--fnv64_prime = 1099511628211 -fnv64_offset = 14695981039346656037 --fnv64_octet :: Word8 -> Word64 -> Word64-fnv64_octet octet old = fnv64_prime * (old `xor` fromIntegral octet)------------------------------------------------------------------------------------- 64 bit--fnv64_word32 :: Word32 -> Word64 -> Word64-fnv64_word32 w = fnv64_octet a . fnv64_octet b . fnv64_octet c . fnv64_octet d where- a = fromIntegral (255 .&. ( w ))- b = fromIntegral (255 .&. (shiftR w 8))- c = fromIntegral (255 .&. (shiftR w 16))- d = fromIntegral (255 .&. (shiftR w 24))--{--fnv64_word24 :: Word32 -> Word64 -> Word64-fnv64_word24 w = fnv64_octet a . fnv64_octet b . fnv64_octet c where- a = fromIntegral (255 .&. ( w ))- b = fromIntegral (255 .&. (shiftR w 8))- c = fromIntegral (255 .&. (shiftR w 16))--}- -fnv64_word16 :: Word16 -> Word64 -> Word64-fnv64_word16 w = fnv64_octet a . fnv64_octet b where- a = fromIntegral (255 .&. ( w ))- b = fromIntegral (255 .&. (shiftR w 8))--fnv64_word64 :: Word64 -> Word64 -> Word64-fnv64_word64 w = fnv64_word32 a . fnv64_word32 b where- a = fromIntegral (0xffffffff .&. ( w ))- b = fromIntegral (0xffffffff .&. (shiftR w 32))------------------------------------------------------------------------------------
− Data/Generics/Fixplate/Hash/Table.hs
@@ -1,109 +0,0 @@- --- | Simple hash tables, implemented as @Map hash (Map key value)@. --- --- To be Haskell98 compatible (no multi-param type classes), when constructing --- a new hash table, we have to support the function computing (or just fetching, if --- it is cached) the hash value. This function is then stored in the data type. --- -module Data.Generics.Fixplate.Hash.Table - ( HashTable - , getHashValue , unHashTable - -- * Construction and deconstruction - , empty , singleton - , fromList , toList - , bag - -- * Membership - , lookup , member - -- * Insert - , insert , insertWith - ) - where - --------------------------------------------------------------------------------- - -import Prelude hiding ( lookup ) - -import Data.List ( foldl' ) - -import qualified Data.Map as Map ; import Data.Map (Map) --- import qualified Data.Set as Set ; import Data.Set (Set) - --------------------------------------------------------------------------------- --- helper functions - -mapInsertWith :: Ord k => (a -> v) -> (a -> v -> v) -> k -> a -> Map k v -> Map k v -mapInsertWith f g k x = x `seq` Map.alter worker k where - worker Nothing = Just $! (f x) - worker (Just y) = y `seq` (Just $! (g x y)) - -mapIsSingleton :: Map k v -> Maybe (k,v) -mapIsSingleton table = if Map.size table == 1 - then let [(k,v)] = Map.toList table in Just (k,v) - else Nothing - -mapIsSingleton_ :: Map k v -> Maybe v -mapIsSingleton_ table = if Map.size table == 1 - then let [(_,v)] = Map.toList table in Just v - else Nothing - --------------------------------------------------------------------------------- - ---newtype HashTable hash k v = HashTable { unHashTable :: Map hash (Map k v) } - -data HashTable hash k v = HashTable - { getHashValue :: k -> hash - , unHashTable :: Map hash (Map k v) - } - -empty :: (Ord hash, Ord k) => (k -> hash) -> HashTable hash k v -empty gethash = HashTable gethash (Map.empty) - -singleton :: (Ord hash, Ord k) => (k -> hash) -> k -> v -> HashTable hash k v -singleton gethash k v = HashTable gethash $ Map.singleton h (Map.singleton k v) where - h = gethash k - -fromList :: (Ord hash, Ord k) => (k -> hash) -> [(k,v)] -> HashTable hash k v -fromList gethash = foldl' (\old (k,v) -> insert k v old) (empty gethash) - -toList :: Ord k => HashTable hash k v -> [(k,v)] -toList (HashTable _ table) = concat [ Map.toList sub | sub <- Map.elems table ] - --------------------------------------------------------------------------------- - -lookup :: (Ord hash, Ord k) => k -> HashTable hash k v -> Maybe v -lookup key (HashTable gethash table) = - case Map.lookup h table of - Just sub -> case mapIsSingleton_ sub of - Just v -> Just v - Nothing -> Map.lookup key sub - Nothing -> Nothing - where - h = gethash key - -member :: (Ord hash, Ord k) => k -> HashTable hash k v -> Bool -member key table = case lookup key table of - Just _ -> True - Nothing -> False - --------------------------------------------------------------------------------- - -insert :: (Ord hash, Ord k) => k -> v -> HashTable hash k v -> HashTable hash k v -insert k v (HashTable gethash table) = HashTable gethash $ mapInsertWith f g h v table where - h = gethash k - f v = Map.singleton k v - g v sub = Map.insert k v sub - -insertWith :: (Ord hash, Ord k) => (a -> v) -> (a -> v -> v) -> k -> a -> HashTable hash k v -> HashTable hash k v -insertWith ff gg k x (HashTable gethash table) = HashTable gethash $ mapInsertWith f g h x table where - h = gethash k - f x = Map.singleton k (ff x) - g x sub = mapInsertWith ff gg k x sub - --------------------------------------------------------------------------------- - --- | Creates a multi-set from a list. -bag :: (Ord hash, Ord k) => (k -> hash) -> [k] -> HashTable hash k Int -bag gethash = foldl' (\old k -> insertWith id (+) k 1 old) (empty gethash) - --------------------------------------------------------------------------------- -
Data/Generics/Fixplate/Misc.hs view
@@ -5,7 +5,12 @@ -------------------------------------------------------------------------------- import Prelude hiding (mapM,mapM_) + +import Data.List ( sortBy , groupBy ) +import Data.Ord + import Data.Traversable + --import Control.Monad (liftM) --import Control.Monad.Trans.State @@ -22,6 +27,21 @@ | First a | Both a b deriving Show + +-------------------------------------------------------------------------------- + +equating :: Eq b => (a -> b) -> a -> a -> Bool +equating f x y = f x == f y + +groupSortOn :: Ord b => (a -> b) -> [a] -> [[a]] +groupSortOn f xs = groupBy (equating f) $ sortBy (comparing f) xs + +mapGroupSortOn :: Ord b => (a -> b) -> (a -> c) -> [a] -> [(b,[c])] +mapGroupSortOn f g = mapGroupSortOn' f (map g) + +mapGroupSortOn' :: Ord b => (a -> b) -> ([a] -> c) -> [a] -> [(b,c)] +mapGroupSortOn' f g xs = map h $ groupBy (equating f) $ sortBy (comparing f) xs where + h ys = (f (head ys), g ys) --------------------------------------------------------------------------------
Data/Generics/Fixplate/Morphisms.hs view
@@ -15,7 +15,7 @@ import Data.Char ( ord ) import Data.List ( intercalate ) import Test.QuickCheck -import Data.Generics.Fixplate.Traversals +-- import Data.Generics.Fixplate.Traversals import Data.Generics.Fixplate.Test.Tools #endif
− Data/Generics/Fixplate/Structure.hs
@@ -1,53 +0,0 @@--{-# LANGUAGE CPP, Rank2Types #-}---- it seems that older GHCs do not like multiple LANGUAGE pragmas?--- {- LANGUAGE CPP -}--- ifdef __GLASGOW_HASKELL__--- {- LANGUAGE Rank2Types -}--- endif---- | Changing the structure of a tree.-module Data.Generics.Fixplate.Structure - ( NatTrafo- , restructure- , liftAnn- )- where- -----------------------------------------------------------------------------------import Data.Generics.Fixplate.Base-------------------------------------------------------------------------------------- #ifdef __GLASGOW_HASKELL__---- | The type of natural transformations.-type NatTrafo f g = forall a. (f a -> g a)---- | Changing the structure of a tree.-restructure :: Functor f => NatTrafo f g -> Mu f -> Mu g-restructure trafo = go where- go = Fix . trafo . fmap go . unFix---- #else --{--data NatTrafo f g = NatTrafo (f Int) (g Int) -- fake and opaque data type---- | Unfortunately, this function requires Rank2Types,--- thus we only provide it for GHC.-restructure :: Functor f => NatTrafo f g -> Mu f -> Mu g-restructure = error "restructure: this operation requires Rank2Types"--}---- #endif- ------------------------------------------------------------------------------------- | Lifting natural transformations to annotations.-liftAnn :: (f e -> g e) -> Ann f a e -> Ann g a e-liftAnn trafo (Ann a x) = Ann a (trafo x)----------------------------------------------------------------------------------
Data/Generics/Fixplate/Tests.hs view
@@ -1,5 +1,6 @@ -- | Run all the tests +{-# LANGUAGE CPP #-} module Data.Generics.Fixplate.Tests where -------------------------------------------------------------------------------- @@ -9,13 +10,17 @@ import Data.Generics.Fixplate.Morphisms import Data.Generics.Fixplate.Attributes import Data.Generics.Fixplate.Zipper -import Data.Generics.Fixplate.Structure +import Data.Generics.Fixplate.Trie import Data.Generics.Fixplate.Test.Tools import Data.Generics.Fixplate.Test.Instances import Test.QuickCheck +#ifdef WITH_UTILITY_MODULES +import Data.Generics.Fixplate.Util.Hash.Table +#endif + -------------------------------------------------------------------------------- run_all_tests :: IO () @@ -27,5 +32,9 @@ putStrLn "tests for Attributes..." ; runtests_Attributes putStrLn "tests for zippers..." ; runtests_Zipper putStrLn "tests for morphisms..." ; runtests_Morphisms + putStrLn "tests for tries..." ; runtests_Trie +#ifdef WITH_UTILITY_MODULES + putStrLn "tests for hash tables..." ; runtests_HashTable +#endif --------------------------------------------------------------------------------
Data/Generics/Fixplate/Traversals.hs view
@@ -1,5 +1,28 @@ -- | Uniplate-style traversals. +-- +-- Toy example: Consider our favourite data type +-- +-- > data Expr e +-- > = Kst Int +-- > | Var String +-- > | Add e e +-- > deriving (Eq,Show,Functor,Foldable,Traversable) +-- > +-- > instance ShowF Expr where showsPrecF = showsPrec +-- +-- and write a function simplifying additions with zero: +-- +-- > simplifyAdd :: Mu Expr -> Mu Expr +-- > simplifyAdd = transform worker where +-- > worker expr = case expr of +-- > Fix (Add x (Fix (Kst 0))) -> x -- 0+x = x +-- > Fix (Add (Fix (Kst 0)) y) -> y -- x+0 = 0 +-- > _ -> expr +-- +-- Unfortunately, all these 'Fix' wrappers are rather ugly; but they are straightforward to put in, +-- and in principle one could use Template Haskell quasi-quotation to generate patterns. +-- {-# LANGUAGE CPP #-} module Data.Generics.Fixplate.Traversals where @@ -63,10 +86,11 @@ Fix y <- h x liftM Fix (mapM go y) --- | Non-recursive top-down transformation. +-- | Non-recursive top-down transformation. This is basically just 'fmap'. descend :: Functor f => (Mu f -> Mu f) -> Mu f -> Mu f descend h = Fix . fmap h . unFix +-- | Similarly, this is basically just 'mapM'. descendM :: (Traversable f, Monad m) => (Mu f -> m (Mu f)) -> Mu f -> m (Mu f) descendM action = liftM Fix . mapM action . unFix @@ -78,7 +102,22 @@ rewriteM :: (Traversable f, Monad m) => (Mu f -> m (Maybe (Mu f))) -> Mu f -> m (Mu f) rewriteM h = transformM g where g x = h x >>= \y -> maybe (return x) (rewriteM h) y + +-------------------------------------------------------------------------------- +-- * Structure change +-- | Bottom-up transformation (typically \"shallow\", that is, restricted to a single level) +-- which can change the structure functor (actually 'transform' is a special case of this). +restructure :: Functor f => (f (Mu g) -> g (Mu g)) -> Mu f -> Mu g +restructure h = go where + go = Fix . h . fmap go . unFix + +restructureM :: (Traversable f, Monad m) => (f (Mu g) -> m (g (Mu g))) -> Mu f -> m (Mu g) +restructureM action = go where + go (Fix x) = do + y <- mapM go x + liftM Fix (action y) + -------------------------------------------------------------------------------- -- * Context
+ Data/Generics/Fixplate/Trie.hs view
@@ -0,0 +1,477 @@+ +-- | Generalized tries. \"Normal\" tries encode finite maps from lists to arbitrary values, where the +-- common prefixes are shared. Here we do the same for trees, generically. +-- +-- See also +-- +-- * Connelly, Morris: A generalization of the trie data structure +-- +-- * Ralf Hinze: Generalizing Generalized Tries +-- +-- This module should be imported qualified. +-- + +{-# LANGUAGE CPP #-} +module Data.Generics.Fixplate.Trie + ( Trie + -- * Construction \/ deconstruction + , empty , singleton + , fromList , toList + , bag , universeBag + -- * Lookup + , lookup + -- * Insertion \/ deletion + , insert , insertWith + , delete , update + -- * Set operations + , intersection , intersectionWith + , union , unionWith + , difference , differenceWith +#ifdef WITH_QUICKCHECK + -- * Tests + , runtests_Trie +#endif + ) + where + +--------------------------------------------------------------------------------- + +import Prelude hiding ( lookup ) + +import Data.Generics.Fixplate.Base +import Data.Generics.Fixplate.Open hiding ( toList ) +import Data.Generics.Fixplate.Traversals ( universe ) + +import qualified Data.Foldable as Foldable +import Data.Foldable hiding ( toList ) + +import Data.Traversable as Traversable + +import qualified Data.Map as Map ; import Data.Map (Map) + +#ifdef WITH_QUICKCHECK +import Test.QuickCheck +import Data.Generics.Fixplate.Test.Tools +import Data.Generics.Fixplate.Misc +import Data.List ( sort , group , nubBy , nub , (\\) , foldl' ) +import Control.Applicative ( (<$>) ) +import Debug.Trace +#endif + +--------------------------------------------------------------------------------- + +-- | Creates a trie-multiset from a list of trees. +bag :: (Functor f, Foldable f, OrdF f) => [Mu f] -> Trie f Int +bag ts = Prelude.foldl worker emptyTrie ts where + worker trie tree = trieInsertWith id (+) tree 1 trie + +-- | This is equivalent to +-- +-- > universeBag == bag . universe +-- +-- TODO: more efficient implementation? +universeBag :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f Int +universeBag = bag . universe + +--------------------------------------------------------------------------------- + +empty :: (Functor f, Foldable f, OrdF f) => Trie f a +empty = emptyTrie + +singleton :: (Functor f, Foldable f, OrdF f) => Mu f -> a -> Trie f a +singleton = trieSingleton + +lookup :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f a -> Maybe a +lookup = trieLookup + +insert :: (Functor f, Foldable f, OrdF f) => Mu f -> a -> Trie f a -> Trie f a +insert = trieInsertWith id const + +insertWith :: (Functor f, Foldable f, OrdF f) => (a -> b) -> (a -> b -> b) -> Mu f -> a -> Trie f b -> Trie f b +insertWith = trieInsertWith + +update :: (Functor f, Foldable f, OrdF f) => (a -> Maybe a) -> Mu f -> Trie f a -> Trie f a +update = trieUpdate + +delete :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f a -> Trie f a +delete = trieUpdate (const Nothing) + +-- | TODO: more efficient implementation? +fromList :: (Traversable f, OrdF f) => [(Mu f, a)] -> Trie f a +fromList ts = Prelude.foldl worker emptyTrie ts where + worker trie (tree,value) = trieInsertWith id const tree value trie + +toList :: (Traversable f, OrdF f) => Trie f a -> [(Mu f, a)] +toList = trieToList + +intersection :: (Functor f, Foldable f, OrdF f) => Trie f a -> Trie f b -> Trie f a +intersection = trieIntersectionWith const + +intersectionWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> c) -> Trie f a -> Trie f b -> Trie f c +intersectionWith = trieIntersectionWith + +-- | Union is left-biased: +-- +-- > union == unionWith const +-- +union :: (Functor f, Foldable f, OrdF f) => Trie f a -> Trie f a -> Trie f a +union = trieUnionWith const + +unionWith :: (Functor f, Foldable f, OrdF f) => (a -> a -> a) -> Trie f a -> Trie f a -> Trie f a +unionWith = trieUnionWith + +difference :: (Functor f, Foldable f, OrdF f) => Trie f a -> Trie f b -> Trie f a +difference = trieDifferenceWith (\_ _ -> Nothing) + +differenceWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> Maybe a) -> Trie f a -> Trie f b -> Trie f a +differenceWith = trieDifferenceWith + +--------------------------------------------------------------------------------- + +-- | 'Trie' is an efficient(?) implementation of finite maps from @(Mu f)@ to an arbitrary type @v@. +newtype Trie f v = Trie { unTrie :: Map (HoleF f) (Chain f v) } + +data Chain f v + = Value v + | Chain (Trie f (Chain f v)) + +-- this is only to be able to define an Ord instance +newtype HoleF f = HoleF { unHoleF :: f Hole } + +instance EqF f => Eq (HoleF f) where (==) (HoleF x) (HoleF y) = equalF x y +instance OrdF f => Ord (HoleF f) where compare (HoleF x) (HoleF y) = compareF x y + +emptyTrie :: (Functor f, Foldable f, OrdF f) => Trie f v +emptyTrie = Trie (Map.empty) + +--------------------------------------------------------------------------------- + +trieLookup :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f v -> Maybe v +trieLookup (Fix t) (Trie trie) = + case Map.lookup (HoleF s) trie of + Nothing -> Nothing + Just chain -> chainLookup (Foldable.toList t) chain + where + s = fmap (const Hole) t + +chainLookup :: (Functor f, Foldable f, OrdF f) => [Mu f] -> Chain f v -> Maybe v +chainLookup [] chain = case chain of { Value x -> Just x ; _ -> error "chainLookup: shouldn't happen #1" } +chainLookup (k:ks) chain = case chain of + Chain sub -> case trieLookup k sub of + Just chain -> chainLookup ks chain + Nothing -> Nothing + Value _ -> error "chainLookup: shouldn't happen #2" + +--------------------------------------------------------------------------------- + +chainSingleton :: (Functor f, Foldable f, OrdF f) => [Mu f] -> a -> Chain f a +chainSingleton trees x = go trees where + go [] = Value x + go (t:ts) = Chain (trieSingleton t (go ts)) + +trieSingleton :: (Functor f, Foldable f, OrdF f) => Mu f -> a -> Trie f a +trieSingleton (Fix t) x = Trie $ Map.singleton (HoleF s) (chainSingleton (Foldable.toList t) x) where + s = fmap (const Hole) t + +--------------------------------------------------------------------------------- + +mapInsertWith :: Ord k => (a -> v) -> (a -> v -> v) -> k -> a -> Map k v -> Map k v +mapInsertWith f g k x = x `seq` Map.alter worker k where + worker Nothing = Just $! (f x) + worker (Just y) = y `seq` (Just $! (g x y)) + +trieInsertWith :: (Functor f, Foldable f, OrdF f) => (a -> b) -> (a -> b -> b) -> Mu f -> a -> Trie f b -> Trie f b +trieInsertWith uf ug (Fix t) value (Trie trie) = Trie $ mapInsertWith wf wg (HoleF s) value trie where + wf z = chainSingleton (Foldable.toList t) (uf z) + wg z chain = chainInsertWith uf ug (Foldable.toList t) z chain + s = fmap (const Hole) t + +chainInsertWith :: (Functor f, Foldable f, OrdF f) => (a -> b) -> (a -> b -> b) -> [Mu f] -> a -> Chain f b -> Chain f b +chainInsertWith uf ug trees x chain = go trees chain where + go ts chn = case ts of + [] -> case chn of + Value y -> Value (ug x y) + Chain _ -> error "chainInsertWith: shouldn't happen #1" + (t:ts) -> case chn of + Chain trie -> Chain $ trieInsertWith wf wg t x trie where + wf z = chainSingleton ts (uf z) + wg z c = chainInsertWith uf ug ts z c + Value _ -> error "chainInsertWith: shouldn't happen #2" + +--------------------------------------------------------------------------------- + +trieUpdate :: (Functor f, Foldable f, OrdF f) => (a -> Maybe a) -> Mu f -> Trie f a -> Trie f a +trieUpdate user (Fix t) (Trie trie) = Trie $ Map.update worker (HoleF s) trie where + worker chain = chainUpdate user (Foldable.toList t) chain + s = fmap (const Hole) t + +chainUpdate :: (Functor f, Foldable f, OrdF f) => (a -> Maybe a) -> [Mu f] -> Chain f a -> Maybe (Chain f a) +chainUpdate user = go where + go trees chain = case trees of + [] -> case chain of + Value x -> case user x of + Just y -> Just (Value y) + Nothing -> Nothing + Chain _ -> error "chainUpdate: shouldn't happen #1" + (t:ts) -> case chain of + Chain trie -> Just $ Chain $ trieUpdate (go ts) t trie + Value _ -> error "chainInsertWith: shouldn't happen #2" + +--------------------------------------------------------------------------------- + +trieToList :: (Traversable f, OrdF f) => Trie f a -> [(Mu f, a)] +trieToList (Trie trie) = + [ (Fix (builder key ts), val) + | (HoleF key, chain) <- Map.toList trie + , (ts, val) <- chainToList chain + ] + +chainToList :: (Traversable f, OrdF f) => Chain f a -> [([Mu f], a)] +chainToList = go where + go chain = case chain of + Value x -> [([],x)] + Chain trie -> + [ (t:ts, val) + | (t ,ch ) <- trieToList trie + , (ts,val) <- go ch + ] + +--------------------------------------------------------------------------------- + +chainIntersectionWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> c) -> Chain f a -> Chain f b -> Chain f c +chainIntersectionWith f (Value x ) (Value y ) = Value (f x y) +chainIntersectionWith f (Chain t1) (Chain t2) = Chain (trieIntersectionWith (chainIntersectionWith f) t1 t2) +chainIntersectionWith _ _ _ = error "chainIntersectionWith: shouldn't happen" + +trieIntersectionWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> c) -> Trie f a -> Trie f b -> Trie f c +trieIntersectionWith f (Trie trie1) (Trie trie2) = Trie (Map.intersectionWith worker trie1 trie2) where + worker chain1 chain2 = chainIntersectionWith f chain1 chain2 + +--------------------------------------------------------------------------------- + +chainUnionWith :: (Functor f, Foldable f, OrdF f) => (a -> a -> a) -> Chain f a -> Chain f a -> Chain f a +chainUnionWith f (Value x ) (Value y ) = Value (f x y) +chainUnionWith f (Chain t1) (Chain t2) = Chain (trieUnionWith (chainUnionWith f) t1 t2) +chainUnionWith _ _ _ = error "chainUnionWith: shouldn't happen" + +trieUnionWith :: (Functor f, Foldable f, OrdF f) => (a -> a -> a) -> Trie f a -> Trie f a -> Trie f a +trieUnionWith f (Trie trie1) (Trie trie2) = Trie (Map.unionWith worker trie1 trie2) where + worker chain1 chain2 = chainUnionWith f chain1 chain2 + +--------------------------------------------------------------------------------- + +chainDifferenceWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> Maybe a) -> Chain f a -> Chain f b -> Maybe (Chain f a) +chainDifferenceWith f (Value x ) (Value y ) = case f x y of + Just z -> Just (Value z) + Nothing -> Nothing +chainDifferenceWith f (Chain t1) (Chain t2) = Just $ Chain (trieDifferenceWith (chainDifferenceWith f) t1 t2) +chainDifferenceWith _ _ _ = error "chainDifferenceWith: shouldn't happen" + +trieDifferenceWith :: (Functor f, Foldable f, OrdF f) => (a -> b -> Maybe a) -> Trie f a -> Trie f b -> Trie f a +trieDifferenceWith f (Trie trie1) (Trie trie2) = Trie (Map.differenceWith worker trie1 trie2) where + worker chain1 chain2 = chainDifferenceWith f chain1 chain2 + +--------------------------------------------------------------------------------- +-- Tests + +#ifdef WITH_QUICKCHECK + +runtests_Trie :: IO () +runtests_Trie = do + quickCheck prop_difference + quickCheck prop_differenceWith + quickCheck prop_union + quickCheck prop_intersection + + quickCheck prop_unibag_naive + quickCheck prop_fromList_naive + quickCheck prop_bag + quickCheck prop_bag_b + quickCheck prop_fromList_toList + quickCheck prop_multiSetToList_b + quickCheck prop_insert + quickCheck prop_delete + quickCheck prop_update + quickCheck prop_insert_delete + quickCheck prop_delete_insert + quickCheck prop_lookup + quickCheck prop_lookup_notfound + quickCheck prop_singleton + +-------------------- + +newtype Multiplicity = Multiplicity { unMultiplicity :: Int } deriving (Eq,Ord,Show) + +instance Arbitrary Multiplicity where + arbitrary = do + n <- choose (1, 7) + return (Multiplicity n) + +newtype MultiSet = MultiSet { unMultiSet :: [(Multiplicity, FixT Label)] } deriving (Eq,Ord,Show) + +instance Arbitrary MultiSet where arbitrary = MultiSet <$> arbitrary + +multiSetToList :: MultiSet -> [FixT Label] +multiSetToList (MultiSet mxs) = go mxs where + go [] = [] + go ((Multiplicity n, x):rest) = replicate n x ++ go rest + +multiSetToList_b :: MultiSet -> [FixT Label] +multiSetToList_b (MultiSet mxs) = go mxs [] where + go [] [] = [] + go [] ys = go ys [] + go ((Multiplicity n, x):rest) ys = if n>0 + then x : go rest ( (Multiplicity (n-1), x) : ys ) + else go rest ys + +newtype FiniteMap = FiniteMap { unFiniteMap :: [(FixT Label,Char)] } deriving (Eq,Ord,Show) + +instance Arbitrary FiniteMap where arbitrary = (FiniteMap . nubBy (equating fst)) <$> arbitrary + +type TrieT = Trie (TreeF Label) Char + +finiteMap :: FiniteMap -> TrieT +finiteMap (FiniteMap fmap) = fromList fmap + +-------------------- + +fromListNaive :: (Traversable f, OrdF f) => [(Mu f, a)] -> Trie f a +fromListNaive ts = Prelude.foldl worker emptyTrie ts where + worker trie (tree,value) = trieInsertWith id const tree value trie + +universeBagNaive :: (Functor f, Foldable f, OrdF f) => Mu f -> Trie f Int +universeBagNaive = bag . universe + +mapBag :: Ord a => [a] -> Map a Int +mapBag xs = Data.List.foldl' f Map.empty xs where + f old x = Map.insertWith (+) x 1 old + +-------------------- + +prop_unibag_naive :: FixT Label -> Bool +prop_unibag_naive tree = toList (universeBag tree) == toList (universeBagNaive tree) + +prop_fromList_naive :: FiniteMap -> Bool +prop_fromList_naive (FiniteMap list) = toList (fromList list) == toList (fromListNaive list) + +prop_bag :: MultiSet -> Bool +prop_bag mset = (sort $ toList $ bag $ multiSetToList mset) == sort (map f $ unMultiSet mset) where + f (Multiplicity k, x) = (x,k) + +prop_bag_b :: MultiSet -> Bool +prop_bag_b mset = (sort $ toList $ bag $ multiSetToList_b mset) == sort (map f $ unMultiSet mset) where + f (Multiplicity k, x) = (x,k) + +prop_fromList_toList :: FiniteMap -> Bool +prop_fromList_toList (FiniteMap list) = sort (toList (fromList list)) == sort list + +prop_multiSetToList_b :: MultiSet -> Bool +prop_multiSetToList_b mset = toList (bag (multiSetToList mset)) == toList (bag (multiSetToList_b mset)) + +prop_insert :: FixT Label -> Char -> FiniteMap -> Bool +prop_insert key ch (FiniteMap list) = sort (toList (insert key ch trie)) == sort ((key,ch) : toList trie) where + trie = fromList list + +prop_delete :: Int -> FiniteMap -> Bool +prop_delete i (FiniteMap list) = (n==0) || (toList (delete key trie) == toList trie \\ [(key,value)]) where + trie = fromList list + n = length list + k = mod i n + (key,value) = list!!k + +prop_update :: Char -> Int -> FiniteMap -> Bool +prop_update new i (FiniteMap list) = (n==0) || (toList (update f key trie) == replace (toList trie)) where + trie = fromList list + n = length list + k = mod i n + (key,value) = list!!k + replace [] = [] + replace (this@(k,x):rest) = if k==key + then case f x of + Nothing -> rest + Just y -> (k,y) : replace rest + else this : replace rest + f old = if old < 'A' then Nothing else Just new + +prop_insert_delete :: FixT Label -> Char -> FiniteMap -> Bool +prop_insert_delete key ch (FiniteMap list) = toList (delete key (insert key ch trie)) == toList trie where + trie = delete key (fromList list) -- ! + +prop_delete_insert :: Int -> FiniteMap -> Bool +prop_delete_insert i (FiniteMap list) = (n==0) || (toList (insert key value (delete key trie)) == toList trie) where + trie = fromList list + n = length list + k = mod i n + (key,value) = list!!k + +prop_lookup :: Int -> FiniteMap -> Bool +prop_lookup i (FiniteMap list) = (n==0) || (Just value == lookup key trie) where + trie = fromList list + n = length list + k = mod i n + (key,value) = list!!k + +prop_lookup_notfound :: FixT Label -> FiniteMap -> Bool +prop_lookup_notfound key (FiniteMap list) = lookup key trie == Nothing where + trie = delete key (fromList list) -- !#endif + +prop_singleton :: FixT Label -> Char -> Bool +prop_singleton tree ch = toList (singleton tree ch) == [(tree,ch)] + +prop_intersection :: MultiSet -> Bool +prop_intersection mset = {- trace ("--"++show n++"--") -} (itrie == imap) where + + list = multiSetToList_b mset + n = length list + k = div n 3 + l = div (2*n) 3 + xs = take l list + ys = drop k list + + itrie = sort $ toList $ intersectionWith (+) ( bag xs) ( bag ys) + imap = sort $ Map.toList $ Map.intersectionWith (+) (mapBag xs) (mapBag ys) + +prop_union :: MultiSet -> Bool +prop_union mset = {- trace ("--"++show n++"--") -} (utrie == umap) where + + list = multiSetToList_b mset + n = length list + k = div n 3 + l = div (2*n) 3 + xs = take l list + ys = drop k list + + utrie = sort $ toList $ unionWith (+) ( bag xs) ( bag ys) + umap = sort $ Map.toList $ Map.unionWith (+) (mapBag xs) (mapBag ys) + +prop_difference :: MultiSet -> Bool +prop_difference mset = {- trace ("--"++show [length xs , length ys, length dtrie]++"--") -} (dtrie == dmap) where + + list = multiSetToList_b mset + n = length list + k = div n 3 + l = div (2*n) 3 + xs = take l list + ys = drop k list + + dtrie = sort $ toList $ difference ( bag xs) ( bag ys) + dmap = sort $ Map.toList $ Map.difference (mapBag xs) (mapBag ys) + +prop_differenceWith :: MultiSet -> Bool +prop_differenceWith mset = {- trace ("--"++show [length xs , length ys, length dtrie]++"--") -} (dtrie == dmap) where + + list = multiSetToList_b mset + n = length list + k = div n 3 + l = div (2*n) 3 + xs = take l list + ys = drop k list + + f x y = if y<=2 then Just (x+1) else Nothing + + dtrie = sort $ toList $ differenceWith f ( bag xs) ( bag ys) + dmap = sort $ Map.toList $ Map.differenceWith f (mapBag xs) (mapBag ys) + +#endif + +---------------------------------------------------------------------------------
+ Data/Generics/Fixplate/Util/Hash/Class.hs view
@@ -0,0 +1,99 @@+ +-- | Haskell98 polymorphic Hash interface +module Data.Generics.Fixplate.Util.Hash.Class where + +-------------------------------------------------------------------------------- + +import Data.Char +import Data.Word +import Data.Int +import Data.Bits +import Data.List + +-------------------------------------------------------------------------------- + +-- | A type class for hashes. +-- Minimal complete definition: 'emptyHash', 'hashWord8', 'hashHash' and 'showHex'. +class (Eq hash, Ord hash, Hashable hash) => HashValue hash where + + hashWord8 :: Word8 -> hash -> hash + hashWord16 :: Word16 -> hash -> hash + hashWord32 :: Word32 -> hash -> hash + hashWord64 :: Word64 -> hash -> hash + + emptyHash :: hash + hashHash :: hash -> hash -> hash + showHex :: hash -> String + + hashWord32 w = hashWord8 a . hashWord8 b . hashWord8 c . hashWord8 d where + a = fromIntegral (255 .&. ( w )) + b = fromIntegral (255 .&. (shiftR w 8)) + c = fromIntegral (255 .&. (shiftR w 16)) + d = fromIntegral (255 .&. (shiftR w 24)) + + hashWord16 w = hashWord8 a . hashWord8 b where + a = fromIntegral (255 .&. ( w )) + b = fromIntegral (255 .&. (shiftR w 8)) + + hashWord64 w = hashWord32 a . hashWord32 b where + a = fromIntegral (0xffffffff .&. ( w )) + b = fromIntegral (0xffffffff .&. (shiftR w 32)) + +-------------------------------------------------------------------------------- + +-- | A type class of hashable objects. An instance has to compute the hash for +-- /any/ hash function, using the \"base\" types (eg. Word32). +-- +-- Minimal complete definition: 'hashDigest'. The default for 'computeHash' is +-- +-- > computeHash x = hashDigest x emptyHash +-- +class Hashable a where + hashDigest :: HashValue hash => a -> hash -> hash + computeHash :: HashValue hash => a -> hash + + computeHash x = hashDigest x emptyHash + +-------------------------------------------------------------------------------- + +instance Hashable Word8 where hashDigest = hashWord8 +instance Hashable Word16 where hashDigest = hashWord16 +instance Hashable Word32 where hashDigest = hashWord32 +instance Hashable Word64 where hashDigest = hashWord64 + +instance Hashable Int where hashDigest = hashInt +instance Hashable Word where hashDigest = hashWord +instance Hashable Bool where hashDigest = hashBool +instance Hashable Char where hashDigest = hashChar + +-------------------------------------------------------------------------------- + +instance Hashable a => Hashable [a] where + hashDigest xs h = foldl' (flip hashDigest) h xs + +instance (Hashable a, Hashable b) => Hashable (a,b) where + hashDigest (x,y) = hashDigest y . hashDigest x + +instance (Hashable a, Hashable b, Hashable c) => Hashable (a,b,c) where + hashDigest (x,y,z) = hashDigest z . hashDigest y . hashDigest x + +instance (Hashable a, Hashable b, Hashable c, Hashable d) => Hashable (a,b,c,d) where + hashDigest (x,y,z,w) = hashDigest w . hashDigest z . hashDigest y . hashDigest x + +instance (Hashable a, Hashable b, Hashable c, Hashable d, Hashable e) => Hashable (a,b,c,d,e) where + hashDigest (x,y,z,w,u) = hashDigest u . hashDigest w . hashDigest z . hashDigest y . hashDigest x + +-------------------------------------------------------------------------------- + +hashInt :: HashValue hash => Int -> hash -> hash +hashWord :: HashValue hash => Word -> hash -> hash +hashBool :: HashValue hash => Bool -> hash -> hash +hashChar :: HashValue hash => Char -> hash -> hash + +hashInt k = hashWord64 (fromIntegral k) +hashWord k = hashWord64 (fromIntegral k) +hashBool b = hashWord8 (if b then 255 else 0) +hashChar c = hashWord16 (fromIntegral (ord c)) + +-------------------------------------------------------------------------------- +
+ Data/Generics/Fixplate/Util/Hash/FNV/FNV32.hs view
@@ -0,0 +1,94 @@++-- | 32-bit FNV-1a (Fowler-Noll-Vo) hash++{-# LANGUAGE CPP #-}+module Data.Generics.Fixplate.Util.Hash.FNV.FNV32 + ( FNV32(..) + , unFNV32 + ) + where++--------------------------------------------------------------------------------++import Data.Char+import Data.Word+import Data.Bits+-- import Data.Int+-- import Data.List++import Data.Generics.Fixplate.Util.Hash.Class ++--------------------------------------------------------------------------------++newtype FNV32 = FNV32 Word32 deriving (Eq,Ord,Show)++unFNV32 :: FNV32 -> Word32+unFNV32 (FNV32 x) = x++instance Hashable FNV32 where + hashDigest (FNV32 w) = hashDigest w++instance HashValue FNV32 where+ emptyHash = FNV32 fnv32_offset + hashHash (FNV32 w) = hashWord32 w+ showHex (FNV32 w) = showHex32 w+ hashWord8 x (FNV32 w) = FNV32 (fnv32_octet x w)+ hashWord16 x (FNV32 w) = FNV32 (fnv32_word16 x w)+ hashWord32 x (FNV32 w) = FNV32 (fnv32_word32 x w)+ hashWord64 x (FNV32 w) = FNV32 (fnv32_word64 x w)++--------------------------------------------------------------------------------++showHex32 :: Word32 -> String+showHex32 h = reverse $ worker 8 h where+ worker :: Int -> Word32 -> String+ worker 0 0 = []+ worker 0 _ = error "Hash/FNV32/showHex: shouldn't happen"+ worker i w = hexdigit (w .&. 15) : worker (i-1) (shiftR w 4) + hexdigit :: Word32 -> Char+ hexdigit n+ | k>=0 && k<=9 = chr (k+48)+ | otherwise = chr (k+55)+ where k = fromIntegral n++--------------------------------------------------------------------------------+-- FNV-1a hash++fnv32_prime, fnv32_offset :: Word32++fnv32_prime = 16777619 +fnv32_offset = 2166136261 ++fnv32_octet :: Word8 -> Word32 -> Word32+fnv32_octet octet old = fnv32_prime * (old `xor` fromIntegral octet)++--------------------------------------------------------------------------------+-- 32 bit++fnv32_word32 :: Word32 -> Word32 -> Word32+fnv32_word32 w = fnv32_octet a . fnv32_octet b . fnv32_octet c . fnv32_octet d where+ a = fromIntegral (255 .&. ( w ))+ b = fromIntegral (255 .&. (shiftR w 8))+ c = fromIntegral (255 .&. (shiftR w 16))+ d = fromIntegral (255 .&. (shiftR w 24))++{-+fnv32_word24 :: Word32 -> Word32 -> Word32+fnv32_word24 w = fnv32_octet a . fnv32_octet b . fnv32_octet c where+ a = fromIntegral (255 .&. ( w ))+ b = fromIntegral (255 .&. (shiftR w 8))+ c = fromIntegral (255 .&. (shiftR w 16))+-}++fnv32_word16 :: Word16 -> Word32 -> Word32+fnv32_word16 w = fnv32_octet a . fnv32_octet b where+ a = fromIntegral (255 .&. ( w ))+ b = fromIntegral (255 .&. (shiftR w 8))++fnv32_word64 :: Word64 -> Word32 -> Word32+fnv32_word64 w = fnv32_word32 a . fnv32_word32 b where+ a = fromIntegral (0xffffffff .&. ( w ))+ b = fromIntegral (0xffffffff .&. (shiftR w 32))++--------------------------------------------------------------------------------+
+ Data/Generics/Fixplate/Util/Hash/FNV/FNV64.hs view
@@ -0,0 +1,97 @@++-- | 64-bit FNV-1a (Fowler-Noll-Vo) hash++{-# LANGUAGE CPP #-}+module Data.Generics.Fixplate.Util.Hash.FNV.FNV64 + ( FNV64(..) + , unFNV64+ ) + where++--------------------------------------------------------------------------------++import Data.Char+import Data.Word+import Data.Bits+-- import Data.Int+-- import Data.List++import Data.Generics.Fixplate.Util.Hash.Class++--------------------------------------------------------------------------------++instance Hashable FNV64 where + hashDigest (FNV64 w) = hashDigest w++instance HashValue FNV64 where+ emptyHash = FNV64 fnv64_offset + hashHash (FNV64 w) = hashWord64 w+ showHex (FNV64 w) = showHex64 w+ hashWord8 x (FNV64 w) = FNV64 (fnv64_octet x w)+ hashWord16 x (FNV64 w) = FNV64 (fnv64_word16 x w)+ hashWord32 x (FNV64 w) = FNV64 (fnv64_word32 x w)+ hashWord64 x (FNV64 w) = FNV64 (fnv64_word64 x w)+ +--------------------------------------------------------------------------------++newtype FNV64 = FNV64 Word64 deriving (Eq,Ord,Show)++unFNV64 :: FNV64 -> Word64+unFNV64 (FNV64 x) = x++--------------------------------------------------------------------------------++showHex64 :: Word64 -> String+showHex64 h = reverse $ worker 16 h where+ worker :: Int -> Word64 -> String+ worker 0 0 = []+ worker 0 _ = error "Hash/FNV64/showHex: shouldn't happen"+ worker i w = hexdigit (w .&. 15) : worker (i-1) (shiftR w 4) + hexdigit :: Word64 -> Char+ hexdigit n+ | k>=0 && k<=9 = chr (k+48)+ | otherwise = chr (k+55)+ where k = fromIntegral n++--------------------------------------------------------------------------------+-- FNV-1a hash++fnv64_prime, fnv64_offset :: Word64++fnv64_prime = 1099511628211 +fnv64_offset = 14695981039346656037 ++fnv64_octet :: Word8 -> Word64 -> Word64+fnv64_octet octet old = fnv64_prime * (old `xor` fromIntegral octet)++--------------------------------------------------------------------------------+-- 64 bit++fnv64_word32 :: Word32 -> Word64 -> Word64+fnv64_word32 w = fnv64_octet a . fnv64_octet b . fnv64_octet c . fnv64_octet d where+ a = fromIntegral (255 .&. ( w ))+ b = fromIntegral (255 .&. (shiftR w 8))+ c = fromIntegral (255 .&. (shiftR w 16))+ d = fromIntegral (255 .&. (shiftR w 24))++{-+fnv64_word24 :: Word32 -> Word64 -> Word64+fnv64_word24 w = fnv64_octet a . fnv64_octet b . fnv64_octet c where+ a = fromIntegral (255 .&. ( w ))+ b = fromIntegral (255 .&. (shiftR w 8))+ c = fromIntegral (255 .&. (shiftR w 16))+-}+ +fnv64_word16 :: Word16 -> Word64 -> Word64+fnv64_word16 w = fnv64_octet a . fnv64_octet b where+ a = fromIntegral (255 .&. ( w ))+ b = fromIntegral (255 .&. (shiftR w 8))++fnv64_word64 :: Word64 -> Word64 -> Word64+fnv64_word64 w = fnv64_word32 a . fnv64_word32 b where+ a = fromIntegral (0xffffffff .&. ( w ))+ b = fromIntegral (0xffffffff .&. (shiftR w 32))++--------------------------------------------------------------------------------++
+ Data/Generics/Fixplate/Util/Hash/Table.hs view
@@ -0,0 +1,530 @@+ +-- | Hash tables, implemented as a structure similar to @Map hash (Map key value)]@. +-- +-- What this data structure can also give you is a unique value (a @(hash,Int)@ pair) +-- for each key, even during building the table: It is guaranteed to be unique +-- in the past and future lifetime of a single hashtable (that is, one realization +-- of the world-line), among all the keys appearing in that history. +-- +-- Set operations (union, intersection) clearly break this principle; this is +-- resolved by declaring these operations to be /left-biased/, in the sense that +-- they retain the unique values of the left table (so @union t1 t2@ belongs to +-- to @t1@'s world-line, but not to @t2@'s one). +-- +-- If a key is first removed then added back again, it will get a new value. +-- +-- To be Haskell98 compatible (no multi-param type classes), when constructing +-- a new hash table, we have to support the function computing (or just fetching, if +-- it is cached) the hash value. This function is then stored in the data type. +-- + +{-# LANGUAGE CPP #-} +module Data.Generics.Fixplate.Util.Hash.Table + ( HashTable , Bucket(..) , Leaf(..) + , getHashValue , unHashTable + -- * Construction and deconstruction + , empty , singleton + , fromList , toList + , null + , bag + -- * Membership + , lookup , member + -- * Insertion / deletion + , insert , insertWith + , delete + -- * Union + , union , unionWith + , unionsWith , unionsWith' + -- * Intersection + , intersection, intersectionWith + , intersectionsWith , intersectionsWith' + -- * Difference + , difference , differenceWith + -- * Unique indices + , getUniqueIndex + , keysWith + , mapWithUniqueIndices +#ifdef WITH_QUICKCHECK + -- * Tests + , runtests_HashTable + , prop_insert , prop_delete + , prop_insertDelete , prop_deleteInsert + , prop_insertInsert , prop_deleteDelete + , prop_fromListToList + , prop_intersection , prop_intersectionWith + , prop_union , prop_unionWith + , prop_difference , prop_differenceWith + , prop_uniqueValues +#endif + ) + where + +-------------------------------------------------------------------------------- + +import Prelude hiding ( lookup , null ) + +import Data.List ( foldl' ) + +import qualified Data.Map as Map ; import Data.Map (Map) +-- import qualified Data.Set as Set ; import Data.Set (Set) + +#ifdef WITH_QUICKCHECK +import Test.QuickCheck +import Test.QuickCheck.Modifiers +import Data.Generics.Fixplate.Misc +import Data.List ( sort , group , nubBy , nub , (\\) , foldl' , scanl ) +import Control.Monad +import Control.Applicative ( (<$>) ) +import Debug.Trace +#endif + +-------------------------------------------------------------------------------- +-- helper functions + +mapInsertWith :: Ord k => (a -> v) -> (a -> v -> v) -> k -> a -> Map k v -> Map k v +mapInsertWith f g k x = x `seq` Map.alter worker k where + worker Nothing = Just $! (f x) + worker (Just y) = y `seq` (Just $! (g x y)) + +{- +mapIsSingleton :: Map k v -> Maybe (k,v) +mapIsSingleton table = if Map.size table == 1 + then let [(k,v)] = Map.toList table in Just (k,v) + else Nothing + +mapIsSingleton_ :: Map k v -> Maybe v +mapIsSingleton_ table = if Map.size table == 1 + then let [(_,v)] = Map.toList table in Just v + else Nothing +-} + +-------------------------------------------------------------------------------- +-- buckets + +data Leaf v = Leaf {-# UNPACK #-} !Int v -- the index of the key, plus a value +data Bucket k v = Bucket {-# UNPACK #-} !Int !(Map k (Leaf v)) -- the next free index, plus the elements in the bucket + +fromLeaf :: Leaf v -> v +fromLeaf (Leaf _ x) = x + +emptyBucket :: Bucket k v +emptyBucket = Bucket 0 (Map.empty) + +bucketSingleton :: k -> v -> Bucket k v +bucketSingleton k x = Bucket 1 (Map.singleton k (Leaf 0 x)) + +bucketInsert :: Ord k => k -> v -> Bucket k v -> Bucket k v +bucketInsert = bucketInsertWith id const -- not (flip const), since a -> v -> v !!! + +bucketInsertWith :: Ord k => (a -> v) -> (a -> v -> v) -> k -> a -> Bucket k v -> Bucket k v +bucketInsertWith f g k x (Bucket n table) = x `seq` new where + new = Bucket (n+1) (Map.alter worker k table) + worker Nothing = Just $! (Leaf n (f x)) + worker (Just (Leaf j y)) = y `seq` (Just $! (Leaf j (g x y))) + +{- +bucketIsSingleton :: Bucket k v -> Maybe (k,v) +bucketIsSingleton (Bucket _ table) = if Map.size table == 1 + then let [(k,Leaf _ v)] = Map.toList table in Just (k,v) + else Nothing + +bucketIsSingleton_ :: Bucket k v -> Maybe v +bucketIsSingleton_ (Bucket _ table) = if Map.size table == 1 + then let [Leaf _ v] = Map.elems table in Just v + else Nothing +-} + +-------------------------------------------------------------------------------- + +data HashTable hash k v = HashTable + { getHashValue :: k -> hash + , unHashTable :: Map hash (Bucket k v) + } + +empty :: (Ord hash, Ord k) => (k -> hash) -> HashTable hash k v +empty gethash = HashTable gethash (Map.empty) + +singleton :: (Ord hash, Ord k) => (k -> hash) -> k -> v -> HashTable hash k v +singleton gethash k v = HashTable gethash $ Map.singleton h (bucketSingleton k v) where + h = gethash k + +fromList :: (Ord hash, Ord k) => (k -> hash) -> [(k,v)] -> HashTable hash k v +fromList gethash = foldl' (\old (k,v) -> insert k v old) (empty gethash) + +-- | Note that the returned list is ordered by hash, /not/ by keys like 'Data.Map'! +toList :: Ord k => HashTable hash k v -> [(k,v)] +toList (HashTable _ table) = + [ (k,v) + | Bucket _ sub <- Map.elems table + , (k, Leaf _ v) <- Map.toList sub + ] + +null :: (Ord hash, Ord k) => HashTable hash k v -> Bool +null t = case toList t of + [] -> True + _ -> False + +-- | Keys together with their associated unique values +keysWith :: Ord k => (k -> hash -> Int -> a) -> HashTable hash k v -> [a] +keysWith f (HashTable _ table) = + [ f k hash j + | (hash, Bucket _ sub) <- Map.toList table + , (k, Leaf j _) <- Map.toList sub + ] + +-------------------------------------------------------------------------------- + +lookup :: (Ord hash, Ord k) => k -> HashTable hash k v -> Maybe v +lookup key (HashTable gethash table) = + case Map.lookup h table of + Just (Bucket n sub) -> case Map.lookup key sub of + Just (Leaf _ v) -> Just v + Nothing -> Nothing + Nothing -> Nothing + where + h = gethash key + +-- | Look up a unique index, in the form of a @(hash,Int)@ pair, for any key. +-- If the user-supplied function is /injective/, then the result is guaranteed to be uniquely +-- associated to the given key in the past and future history of this table (but of +-- course not unique among different future histories). +-- +getUniqueIndex :: (Ord hash, Ord k) => (hash -> Int -> a) -> k -> HashTable hash k v -> Maybe a +getUniqueIndex f key (HashTable gethash table) = + case Map.lookup h table of + Just bucket@(Bucket _ sub) -> case Map.lookup key sub of + Just (Leaf j _) -> Just (f h j) + Nothing -> Nothing + Nothing -> Nothing + where + h = gethash key + +member :: (Ord hash, Ord k) => k -> HashTable hash k v -> Bool +member key table = case lookup key table of + Just _ -> True + Nothing -> False + +-------------------------------------------------------------------------------- + +insert :: (Ord hash, Ord k) => k -> v -> HashTable hash k v -> HashTable hash k v +insert k v (HashTable gethash table) = HashTable gethash $ mapInsertWith f g h v table where + h = gethash k + f v = bucketSingleton k v + g v sub = bucketInsert k v sub + +insertWith :: (Ord hash, Ord k) => (a -> v) -> (a -> v -> v) -> k -> a -> HashTable hash k v -> HashTable hash k v +insertWith ff gg k x (HashTable gethash table) = HashTable gethash $ mapInsertWith f g h x table where + h = gethash k + f x = bucketSingleton k (ff x) + g x sub = bucketInsertWith ff gg k x sub + +delete :: (Ord hash, Ord k) => k -> HashTable hash k v -> HashTable hash k v +delete k (HashTable gethash table) = HashTable gethash $ Map.alter worker h table where + h = gethash k + worker Nothing = Nothing + worker (Just (Bucket n sub)) = Just $ Bucket n (Map.delete k sub) + +-------------------------------------------------------------------------------- +-- union + +-- | > union == unionWith const +union :: (Ord hash, Ord k) => HashTable hash k a -> HashTable hash k a -> HashTable hash k a +union = unionWith const + +-- | This is unsafe in the sense that the two @getHash@ functions +-- (supplied when the hash tables were created) must agree. The same applies for all the set operations. +-- +-- It is also left-biased in the sense that the unique indices from the left hashtable are retained, +-- while the unique indices from the right hashtable are /changed/. +unionWith :: (Ord hash, Ord k) => (v -> v -> v) -> HashTable hash k v -> HashTable hash k v -> HashTable hash k v +unionWith g (HashTable gethash table1) (HashTable _ table2) = HashTable gethash (Map.unionWith worker table1 table2) + where + worker (Bucket n sub1) (Bucket m sub2) = Bucket (n+m) (Map.unionWith h sub1 $ Map.map offset sub2) where + h (Leaf i x) (Leaf _ y) = Leaf i (g x y) + offset (Leaf j y) = Leaf (n+j) y + +-- | This is unsafe both in the above sense and also that it does not accepts the empty list (for the same reason). +-- The result belongs to the world-line of the first table. +unionsWith :: (Ord hash, Ord k) => (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v +unionsWith g tables = case tables of + [x] -> x + [] -> error "HashTable/unionsWith: empty list" + xs -> foldl1 (unionWith g) xs + +-- | This one accepts the empty list. The empty imput creates a new world-line. +unionsWith' :: (Ord hash, Ord k) => (k -> hash) -> (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v +unionsWith' gethash g tables = case tables of + [x] -> x + [] -> empty gethash + xs -> foldl1 (unionWith g) xs + +-------------------------------------------------------------------------------- +-- intersection + +-- | > intersection == intersectionWith const +intersection :: (Ord hash, Ord k) => HashTable hash k a -> HashTable hash k b -> HashTable hash k a +intersection = intersectionWith const + +-- NOTE the `Map.union` and `Map.difference` here!!!!! +-- This is necessary so that the world-line property remains true: if there is a hash present in the left table +-- but not in the right table, then we have to put an empty bucket in the resulting table while retaining the +-- next unique index value). Unfortunately "Data.Map" does not have a flexible enough set operation to be used here... +intersectionWith :: (Ord hash, Ord k) => (a -> b -> c) -> HashTable hash k a -> HashTable hash k b -> HashTable hash k c +intersectionWith g (HashTable gethash table1) (HashTable _ table2) = + HashTable gethash (Map.union a_minus_b a_cap_b) {- disjoint union -} where + a_cap_b = Map.intersectionWith cap_worker table1 table2 + a_minus_b = Map.map empty_worker (Map.difference table1 table2) + + cap_worker (Bucket n sub1) (Bucket _ sub2) = Bucket n (Map.intersectionWith h sub1 sub2) where + h (Leaf i x) (Leaf _ y) = Leaf i (g x y) + + -- empty_worker :: Bucket k a -> Bucket k c + empty_worker (Bucket n sub1) = Bucket n (Map.empty) + +intersectionsWith :: (Ord hash, Ord k) => (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v +intersectionsWith g tables = case tables of + [x] -> x + [] -> error "HashTable/intersectionWith: empty list" + xs -> foldl1 (intersectionWith g) xs + +intersectionsWith' :: (Ord hash, Ord k) => (k -> hash) -> (v -> v -> v) -> [HashTable hash k v] -> HashTable hash k v +intersectionsWith' gethash g tables = case tables of + [x] -> x + [] -> empty gethash + xs -> foldl1 (intersectionWith g) xs + +-------------------------------------------------------------------------------- +-- difference + +difference :: (Ord hash, Ord k) => HashTable hash k a -> HashTable hash k b -> HashTable hash k a +difference = differenceWith (\_ _ -> Nothing) + +differenceWith :: (Ord hash, Ord k) => (a -> b -> Maybe a) -> HashTable hash k a -> HashTable hash k b -> HashTable hash k a +differenceWith g (HashTable gethash table1) (HashTable _ table2) = HashTable gethash (Map.differenceWith worker table1 table2) + where + worker (Bucket n sub1) (Bucket _ sub2) = Just (Bucket n (Map.differenceWith h sub1 sub2)) where + h (Leaf i x) (Leaf _ y) = case g x y of + Just z -> Just (Leaf i z) + Nothing -> Nothing + +-------------------------------------------------------------------------------- + +-- | Creates a multi-set from a list. +bag :: (Ord hash, Ord k) => (k -> hash) -> [k] -> HashTable hash k Int +bag gethash = foldl' (\old k -> insertWith id (+) k 1 old) (empty gethash) + +-------------------------------------------------------------------------------- + +mapWithUniqueIndices :: (Ord hash, Ord k) => (hash -> Int -> a -> b) -> HashTable hash k a -> HashTable hash k b +mapWithUniqueIndices user (HashTable gethash table) = HashTable gethash (Map.mapWithKey worker table) where + worker hash (Bucket n sub) = Bucket n (Map.map g sub) where + g (Leaf j x) = Leaf j (user hash j x) + +-------------------------------------------------------------------------------- +#ifdef WITH_QUICKCHECK +-- * tests + +runtests_HashTable :: IO () +runtests_HashTable = do + quickCheck prop_insert + quickCheck prop_delete + quickCheck prop_insertDelete + quickCheck prop_deleteInsert + quickCheck prop_insertInsert + quickCheck prop_deleteDelete + quickCheck prop_fromListToList + quickCheck prop_intersection + quickCheck prop_intersectionWith + quickCheck prop_union + quickCheck prop_unionWith + quickCheck prop_difference + quickCheck prop_differenceWith + replicateM_ 5 $ quickCheck prop_uniqueValues +-- quickCheck prop_ +-- quickCheck prop_ + +------------------------- + +debug x y = trace ("-- " ++ show x ++ " --") y + +newtype Key = Key Int deriving (Eq,Ord,Show) + +instance (Ord k, Ord hash, Show k, Show v) => Show (HashTable hash k v) where + show t = "HashTable<< " ++ show (toList t) ++ " >>" + +instance Arbitrary Key where + arbitrary = do + n <- choose (0, 255) + return (Key n) + +newtype Hash = Hash Int deriving (Eq,Ord,Show) + +calcHash :: Key -> Hash +calcHash (Key k) = Hash (mod k 17) + +newtype Table v = Table (HashTable Hash Key v) deriving Show + +instance Arbitrary v => Arbitrary (Table v) where + arbitrary = do + xs <- arbitrary + let t = fromList calcHash xs + {- debug (length xs) $ -} + return (Table t) + +newtype NonEmptyTable v = NonEmptyTable (HashTable Hash Key v) deriving Show + +instance Arbitrary v => Arbitrary (NonEmptyTable v) where + arbitrary = do + NonEmpty xs <- arbitrary + let t = fromList calcHash xs + {- debug (length xs) $ -} + return (NonEmptyTable t) + +data Pointed v = Pointed (HashTable Hash Key v) (Key,v) deriving Show + +instance Arbitrary v => Arbitrary (Pointed v) where + arbitrary = do + NonEmptyTable t <- arbitrary + let list = toList t + n = length list + i <- choose (0,n-1) + let kv =list!!i + return (Pointed t kv) + +sortedToList :: Ord a => HashTable Hash Key a -> [(Key,a)] +sortedToList = sort . toList + +------------------------- + +data Step v + = Insert Key v + | InsertWith Key v + | Delete Key + | Union (Table v) + | Intersect (Table v) + | Difference (Table v) + deriving Show + +instance Arbitrary v => Arbitrary (Step v) where + arbitrary = do + frequency + [ ( 10 , do { k<-arbitrary ; v<-arbitrary ; return (Insert k v) } ) + , ( 5 , do { k<-arbitrary ; v<-arbitrary ; return (InsertWith k v) } ) + , ( 10 , do { k<-arbitrary ; return (Delete k) } ) + , ( 3 , do { t<-arbitrary ; return (Union t ) } ) + , ( 2 , do { t<-arbitrary ; return (Difference t ) } ) + , ( 1 , do { t<-arbitrary ; return (Intersect t ) } ) + ] + +newtype NoDeleteStep v = NoDeleteStep (Step v) + +instance Arbitrary v => Arbitrary (NoDeleteStep v) where + arbitrary = NoDeleteStep <$> do + frequency + [ ( 10 , do { k<-arbitrary ; v<-arbitrary ; return (Insert k v) } ) + , ( 5 , do { k<-arbitrary ; v<-arbitrary ; return (InsertWith k v) } ) + , ( 3 , do { t<-arbitrary ; return (Union t ) } ) + ] + +step :: (v -> v -> v) -> Step v -> HashTable Hash Key v -> HashTable Hash Key v +step f step old = case step of + Insert k v -> insert k v old + InsertWith k v -> insertWith id f k v old + Delete k -> delete k old + Union (Table t) -> union old t + Intersect (Table t) -> intersection old t + Difference (Table t) -> difference old t + +type History v = [Step v] + +runHistory :: (v -> v -> v) -> History v -> HashTable Hash Key v -> [HashTable Hash Key v] +runHistory f steps ini = scanl (flip (step f)) ini steps + +data U = U Hash Int deriving (Eq,Ord,Show) + +------------------------- + +prop_insert :: Key -> Char -> Table Char -> Bool +prop_insert k v (Table table) = lookup k (insert k v table) == Just v + +prop_delete :: Pointed Char -> Bool +prop_delete (Pointed table (k,_)) = lookup k (delete k table) == Nothing + +prop_insertInsert :: Key -> Char -> Table Char -> Bool +prop_insertInsert k v (Table table) = toList (insert k v table) == toList (insert k v (insert k v table)) + +prop_deleteDelete :: Pointed Char -> Bool +prop_deleteDelete (Pointed table (k,_)) = toList (delete k table) == toList (delete k (delete k table)) + +prop_insertDelete :: Key -> Char -> Table Char -> Bool +prop_insertDelete k v (Table table) = lookup k (delete k $ insert k v table) == Nothing + +prop_deleteInsert :: Pointed Char -> Bool +prop_deleteInsert (Pointed table (k,v)) = lookup k (insert k v $ delete k table) == Just v + +prop_fromListToList :: [(Key,Char)] -> Bool +prop_fromListToList xs = sortedToList (fromList calcHash xs) == Map.toList (Map.fromList xs) + +prop_intersection :: [(Key,Char)] -> [(Key,Bool)] -> Bool +prop_intersection xs ys = sortedToList (intersection t1 t2) == Map.toList (Map.intersection m1 m2) where + t1 = fromList calcHash xs + t2 = fromList calcHash ys + m1 = Map.fromList xs + m2 = Map.fromList ys + +prop_intersectionWith :: [(Key,Char)] -> [(Key,String)] -> Bool +prop_intersectionWith xs ys = sortedToList (intersectionWith (:) t1 t2) == Map.toList (Map.intersectionWith (:) m1 m2) where + t1 = fromList calcHash xs + t2 = fromList calcHash ys + m1 = Map.fromList xs + m2 = Map.fromList ys + +prop_union :: [(Key,Char)] -> [(Key,Char)] -> Bool +prop_union xs ys = sortedToList (union t1 t2) == Map.toList (Map.union m1 m2) where + t1 = fromList calcHash xs + t2 = fromList calcHash ys + m1 = Map.fromList xs + m2 = Map.fromList ys + +prop_unionWith :: [(Key,String)] -> [(Key,String)] -> Bool +prop_unionWith xs ys = sortedToList (unionWith (++) t1 t2) == Map.toList (Map.unionWith (++) m1 m2) where + t1 = fromList calcHash xs + t2 = fromList calcHash ys + m1 = Map.fromList xs + m2 = Map.fromList ys + +prop_difference :: [(Key,Char)] -> [(Key,Bool)] -> Bool +prop_difference xs ys = sortedToList (difference t1 t2) == Map.toList (Map.difference m1 m2) where + t1 = fromList calcHash xs + t2 = fromList calcHash ys + m1 = Map.fromList xs + m2 = Map.fromList ys + +prop_differenceWith :: [(Key,Char)] -> [(Key,Bool)] -> Bool +prop_differenceWith xs ys = sortedToList (differenceWith f t1 t2) == Map.toList (Map.differenceWith f m1 m2) where + t1 = fromList calcHash xs + t2 = fromList calcHash ys + m1 = Map.fromList xs + m2 = Map.fromList ys + f x b = if b then Just x else Nothing + +-- we try to test whether values are really unique and really constant during a wordline +prop_uniqueValues :: History Float -> Table Float -> Bool +prop_uniqueValues history (Table initial) = areUnique && areInjective {- && ... -} where + worldline = runHistory (\x y -> x-y) history initial :: [HashTable Hash Key Float] + lists = ((flip map) worldline $ \table -> keysWith (\k h j -> (U h j, k)) table) :: [[(U,Key)]] + + -- at each point in time, a single value must appear only once in the table + areUnique = and [ isUnique xs | xs <- lists ] + isUnique uks = let us = map fst uks in sort us == sort (nub us) + + -- taking the whole wordline, it must be true that to a single unique value there is only a single key associated + -- (the opposite is not true, since a key can be deleted then reinserted, gaining a new value) + areInjective = and $ map test $ groupSortOn fst $ concat lists where + test :: [(U,Key)] -> Bool + test xs = (length (groupSortOn fst xs) == 1) -- this is redundant, but hey, we are also testing the test :) + && (length (groupSortOn snd xs) == 1) + + +#endif +--------------------------------------------------------------------------------
fixplate.cabal view
@@ -1,6 +1,6 @@ Name: fixplate-Version: 0.1.4+Version: 0.1.5 Synopsis: Uniplate-style generic traversals for optionally annotated fixed-point types. Description: Uniplate-style generic traversals for fixed-point types, which can be optionally annotated with attributes. We also provide recursion schemes,@@ -26,8 +26,8 @@ Description: Compile with the QuickCheck tests. default: False -Flag withHashing- Description: Include the generic hashing functionality+Flag withUtils+ Description: Include utility modules default: True Flag base4@@ -42,8 +42,9 @@ Build-Depends: base >= 3 && < 4 cpp-options: -DBASE_VERSION=3 - if flag(withHashing)+ if flag(withUtils) Build-Depends: containers+ cpp-options: -DWITH_UTILITY_MODULES if flag(withQuickCheck) Build-Depends: QuickCheck > 2.4@@ -57,17 +58,15 @@ Data.Generics.Fixplate.Attributes Data.Generics.Fixplate.Zipper Data.Generics.Fixplate.Draw+ Data.Generics.Fixplate.Trie+ Data.Generics.Fixplate.Hash - if flag(withHashing)- Exposed-Modules: Data.Generics.Fixplate.Hash- Data.Generics.Fixplate.Hash.Class- Data.Generics.Fixplate.Hash.Table- Data.Generics.Fixplate.Hash.FNV.FNV32- Data.Generics.Fixplate.Hash.FNV.FNV64- - if impl(ghc)||impl(hugs) - Exposed-Modules: Data.Generics.Fixplate.Structure- cpp-options: -DHAS_RANK2TYPES+ if flag(withUtils)+ Exposed-Modules: + Data.Generics.Fixplate.Util.Hash.Class+ Data.Generics.Fixplate.Util.Hash.Table+ Data.Generics.Fixplate.Util.Hash.FNV.FNV32+ Data.Generics.Fixplate.Util.Hash.FNV.FNV64 Other-Modules: Data.Generics.Fixplate.Misc