-- | -- Module : Data.Map.Multikey.Packed -- Copyright : (c) Justus Sagemüller 2017 -- License : GPL v3 -- -- Maintainer : (@) jsagemue $ uni-koeln.de -- Stability : experimental -- Portability : portable -- {-# LANGUAGE CPP #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UnicodeSyntax #-} module Data.Map.Multikey.Packed ( -- * Multikey maps CMap , empty , fromList' , toList , lookup -- * The class of supported keys , Keys(..), KeyKey ) where import Prelude hiding (lookup) import qualified Data.Vector as Arr import qualified Data.Map as Map import Data.Map (Map) import Data.Constraint import Control.Monad import Control.Monad.Trans.State import Control.Arrow (first, second) import Data.Function (on) import Data.Ord (comparing) import Data.List (sortBy, groupBy) import Data.Ratio (Ratio) import Data.Word import Data.Int import Data.Void #if MIN_VERSION_base(4,8,0) import Numeric.Natural #endif import qualified Test.QuickCheck as QC type KeyKey k = Either ( ( k -> (LSubkey k, RSubkey k) , LSubkey k -> RSubkey k -> k ) , (Dict (Keys (LSubkey k), Keys (RSubkey k))) ) (Dict (Ord k)) class Eq k => Keys k where type LSubkey k :: * type LSubkey k = k type RSubkey k :: * type RSubkey k = k useKeys :: KeyKey k useKeys' :: proxy k -> KeyKey k useKeys' _ = useKeys instance Keys Void where useKeys = Right Dict instance Keys () where useKeys = Right Dict instance Keys Int where useKeys = Right Dict instance Keys Int8 where useKeys = Right Dict instance Keys Int16 where useKeys = Right Dict instance Keys Int32 where useKeys = Right Dict instance Keys Int64 where useKeys = Right Dict instance Keys Integer where useKeys = Right Dict instance Keys Word where useKeys = Right Dict instance Keys Word8 where useKeys = Right Dict instance Keys Word16 where useKeys = Right Dict instance Keys Word32 where useKeys = Right Dict instance Keys Word64 where useKeys = Right Dict #if MIN_VERSION_base(4,8,0) instance Keys Natural where useKeys = Right Dict #endif instance Keys Float where useKeys = Right Dict instance Keys Double where useKeys = Right Dict instance Integral a => Keys (Ratio a) where useKeys = Right Dict instance Keys Char where useKeys = Right Dict instance Ord a => Keys [a] where useKeys = Right Dict instance Ord a => Keys (Maybe a) where useKeys = Right Dict instance (Ord a, Ord b) => Keys (Either a b) where useKeys = Right Dict instance (Keys x, Keys y) => Keys (x,y) where type LSubkey (x,y) = x type RSubkey (x,y) = y useKeys = Left ((id, (,)), Dict) instance (Keys x, Keys y, Keys z) => Keys (x,y,z) where type LSubkey (x,y,z) = x type RSubkey (x,y,z) = (y,z) useKeys = Left ((\(x,y,z)->(x,(y,z)), \x (y,z)->(x,y,z)), Dict) instance (Keys x, Keys y, Keys z, Keys w) => Keys (x,y,z,w) where type LSubkey (x,y,z,w) = (x,y) type RSubkey (x,y,z,w) = (z,w) useKeys = Left ((\(x,y,z,w)->((x,y),(z,w)), \(x,y) (z,w)->(x,y,z,w)), Dict) -- | For keys of types like 'Int' or 'String', this container behaves just -- like 'Map.Map'. But for compound types – tuples, in particular – it demands the -- “rectangular property”: if the key @(p,x)@ is in the map and key @(q,y)@ too, -- then keys @(q,x)@ and @(p,y)@ must be present as well. -- -- In other words, such a map can be visualised as a /table/ with all the keys -- on two edges and the values in the middle, like, in case of -- @'CMap' ('Int','String') 'Double'@, -- -- @ -- "bla" "blub" "foo" -- 0 3.8 4.1 2.0 -- 1 3.9 6.3 6.3 -- 5 1.0 11.6 2.2 -- 43 54.1 10.0 10.1 -- @ data CMap k a = FlatMap (Map k Int) (Arr.Vector a) | MKeyMap (CMap (LSubkey k) Int) (CMap (RSubkey k) a) deriving (Functor, Foldable) traverseCMap :: Applicative f => (a -> f b) -> CMap k a -> f (CMap k b) traverseCMap f (FlatMap kks v) = FlatMap kks <$> traverse f v traverseCMap f (MKeyMap msk lvs) = MKeyMap msk <$> traverseCMap f lvs instance Traversable (CMap k) where traverse = traverseCMap size :: CMap k a -> Int size (FlatMap k _) = Map.size k size (MKeyMap msk lsv) = size msk * size lsv lookup :: Keys k => k -> CMap k a -> Maybe a lookup = go 0 where go :: Keys κ => Int -> κ -> CMap κ a -> Maybe a go pix k m = case (useKeys, m) of (Left ((splitKey,_), Dict), MKeyMap msk lsv) | (x,y) <- splitKey k , Just ix <- (pix * size msk +) <$> lookup x msk -> go ix y lsv (Right Dict, FlatMap kks v) | Just i <- (pix * Map.size kks +) <$> Map.lookup k kks , i <- Arr.length v -> Just $ v Arr.! i _ -> Nothing flatFromList :: Ord k => [(k, a)] -> CMap k a flatFromList l = FlatMap kks v where kks = fst <$> vsm vsm = Map.fromAscList [ (k, (i,x)) | (i,(k,x):_) <- zip [0..] . groupBy ((==)`on`fst) $ sortBy (comparing fst) l ] v = Arr.fromList $ snd . snd <$> Map.toList vsm overIndex :: Int -> CMap k a -> CMap k a overIndex pix (FlatMap kks v) = FlatMap kks $ Arr.drop (pix * Map.size kks) v overIndex pix (MKeyMap msk lsv) = MKeyMap msk $ overIndex (pix * size msk) lsv toList :: Keys k => CMap k a -> [(k,a)] toList (FlatMap kks v) = second (v Arr.!) <$> Map.toList kks toList (MKeyMap msk lsv) = case useKeys of Left ((_, combineKeys), Dict) -> [ (combineKeys lk rk, x) | (lk, pix) <- toList msk , (rk, x) <- toList $ overIndex pix lsv ] data KeyStructure k = FlatKey (Map k ()) | MultiKey (KeyStructure (LSubkey k)) (KeyStructure (RSubkey k)) instance ∀ k . (Keys k) => Eq (KeyStructure k) where x == y = case (useKeys, (x, y)) of ( Left ( (_ :: k -> (LSubkey k, RSubkey k), _), Dict ) ,(MultiKey lx rx, MultiKey ly ry)) -> lx == ly && rx == ry (Right Dict, (FlatKey kx, FlatKey ky)) -> kx == ky keyStructure :: CMap k a -> KeyStructure k keyStructure (FlatMap k _) = FlatKey $ const () <$> k keyStructure (MKeyMap msk lsv) = MultiKey (keyStructure msk) (keyStructure lsv) empty :: ∀ k a . Keys k => CMap k a empty = case useKeys' ([]::[k]) of Left (_, Dict) -> MKeyMap empty empty Right _ -> FlatMap Map.empty Arr.empty perfectConcat :: ∀ k a . Keys k => [CMap k a] -> Maybe (CMap k a) perfectConcat [] = Just empty perfectConcat ms@(_:_) | allEq $ keyStructure <$> ms = Just $ cat ms | otherwise = Nothing where cat :: ∀ κ . [CMap κ a] -> CMap κ a cat (MKeyMap msk lsv : qs) = MKeyMap msk . cat $ lsv : map (\(MKeyMap _ lsw) -> lsw) qs cat (FlatMap kks v : qs) = FlatMap kks . Arr.concat $ v : map (\(FlatMap _ w) -> w) qs allEq :: Eq a => [a] -> Bool allEq [] = True allEq (x:xs) = all (==x) xs indices :: Traversable t => t a -> t Int indices q = (`evalState`0) . forM q $ \_ -> state $ \i -> (i,i+1) -- | Build a map, if the given keys fulfill the /rectangular property/. fromList' :: ∀ k a . Keys k => [(k, a)] -> Maybe (CMap k a) fromList' l = case useKeys of Left ((splitKey,_), Dict) -> do let (lKeys, rKeys) = unzip [ ((kl,()), (kr,x)) | (k,x) <- l , let (kl,kr) = splitKey k ] msk :: CMap (LSubkey k) () <- fromList' lKeys lsv <- forM (toList msk) $ \(i,()) -> fromList' $ first (snd . splitKey) <$> filter ((==i) . fst . splitKey . fst) l MKeyMap (indices msk) <$> perfectConcat lsv Right Dict -> Just $ flatFromList l fromFlatMap :: ∀ k a . (Keys k, Ord k) => Map k a -> CMap k a fromFlatMap μπ = case fromList' $ Map.toList μπ of Just ζπ -> ζπ flatArb :: (Keys k, Ord k, QC.Arbitrary k, QC.Arbitrary v) => QC.Gen (CMap k v) flatArb = fromFlatMap <$> QC.arbitrary splitArb :: ( Keys k , QC.Arbitrary (CMap (LSubkey k) (Int->v)) , QC.Arbitrary (CMap (RSubkey k) Int) ) => QC.Gen (CMap k v) splitArb = case useKeys of Left ((_, combineKeys), Dict) -> do lmargin <- QC.arbitrary rmargin <- QC.arbitrary let Just m = fromList' [ (combineKeys k l, f (x::Int)) | (k,f) <- toList lmargin , (l,x) <- toList rmargin ] return m instance QC.Arbitrary v => QC.Arbitrary (CMap Int v) where arbitrary = flatArb instance QC.Arbitrary v => QC.Arbitrary (CMap Integer v) where arbitrary = flatArb instance QC.Arbitrary v => QC.Arbitrary (CMap Double v) where arbitrary = flatArb instance QC.Arbitrary v => QC.Arbitrary (CMap Char v) where arbitrary = flatArb instance (QC.Arbitrary c, Ord c, QC.Arbitrary v) => QC.Arbitrary (CMap [c] v) where arbitrary = flatArb type SplArb k l v = (QC.Arbitrary (CMap k (Int->v)), QC.Arbitrary (CMap l Int)) instance (Keys k, Keys l, SplArb k l v) => QC.Arbitrary (CMap (k,l) v) where arbitrary = splitArb instance (Keys k, Keys l, Keys m, SplArb k (l,m) v) => QC.Arbitrary (CMap (k,l,m) v) where arbitrary = splitArb instance (Keys k, Keys l, Keys m, Keys n, SplArb (k,l) (m,n) v) => QC.Arbitrary (CMap (k,l,m,n) v) where arbitrary = splitArb instance (Show k, Keys k, Show a) => Show (CMap k a) where showsPrec p m = showParen (p>9) $ ("fromList' "++) . showsPrec 11 (toList m) instance ∀ k a . (Keys k, Eq a) => Eq (CMap k a) where FlatMap k v == FlatMap i w = k==i && v==w MKeyMap k v == MKeyMap i w = case useKeys' ([]::[k]) of Left (_, Dict) -> k==i && v==w