linear-maps-0.5: Data/IdMap/Core/Fast.hs
{-# LANGUAGE CPP, MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables, RankNTypes, TypeOperators, GADTs, BangPatterns, EmptyDataDecls #-}
module Data.IdMap.Core.Fast
( module Data.Subtyping
, module Data.TypeInt
, module Control.Functor
-- * Identifiers
, Id
, equalBy
-- * Finite maps and sets
, Map
, Set
, insert
, delete
, lookUp
, member
, union
-- * Unsafe operations
, unsafeInsert
, unsafeEquivalent
-- * Range of sets and maps
, Sets (PlusSet)
, Maps (PlusMap)
-- * Creation of sets, maps and identifiers
, ICC, runICC
, ICCS, runICCS
-- * For internal use
, Maplike, MaplikeClass
) where
------------------------------------
#ifdef __CHECK__
import Data.Control.Kvantum
#else
import Data.Control.Kvantum.Void
#endif
import Data.Array.Simple
import Data.Bits (setBit, clearBit, testBit)
import System.IO.Unsafe (unsafePerformIO)
import GHC.Base (Any)
import Data.Subtyping
import Data.TypeInt
import Control.Functor
import Unsafe.Coerce (unsafeCoerce)
-------------------------------- Interface
-- | Identifiers indexed by @k@. @(Id k)@ can be seen as a set of identifiers.
--
-- The possible identifier indexes form a recursive set. An identifier index is either
--
-- * an uninstantiated type variable (inside invocations of 'runICC' and 'runICCS'), or
--
-- * @(a :|: b)@, where @a@ and @b@ are identifier indexes.
newtype Id k
= Id (Array (Maybe Any))
instance Incl Id where
left = unsafeCoerce
right = unsafeCoerce
-- | Equality check of identifiers.
-- The first map parameter is the witness that the identifiers are sane.
--
-- The first parameter prevents identifiers of type @'Id' (a :|: a)@ which could cause strange runtime behaviour.
-- For example, @('left' x == 'right' x)@ should be @False@ in theory, but during runtime @('left' x)@ and @('right' x)@ are exactly the same identifiers.
equalBy :: Maplike i k a -> Id k -> Id k -> Bool
equalBy !_ (Id a) (Id b) = a == b
-- | Family of finite maps from keys @('Id' k)@ to values @a@.
-- For efficiency reasons, use only with concrete type integers:
--
-- > Map I0 k a
-- > Map I1 k a
-- > Map I2 k a
-- > ...
type Map i k a
= Maplike (M i) k a
data M i
newtype Maplike i k a
= Maplike K
-- | /O(1)/. Insert a new key and value in the map. If the key is already present in the map, the associated value is replaced with the supplied value.
--
-- After insertion, the original map may not be used.
{-# SPECIALIZE insert :: Id k -> a -> Map I0 k a -> Map I0 k a #-}
{-# SPECIALIZE insert :: Id k -> a -> Map I1 k a -> Map I1 k a #-}
{-# SPECIALIZE insert :: Id k -> a -> Map I2 k a -> Map I2 k a #-}
insert :: forall i k a. MaplikeClass i a => Id k -> a -> Maplike i k a -> Maplike i k a
insert !(Id a) x (Maplike k) = unsafePerformIO $ do
k' <- renew "insert" k
set (undefined :: i) (Just x) a
return $ Maplike k'
-- | /O(1)/. Delete a key and its value from the map. When the key is not a member of the map, the original map is returned.
--
-- After deletion, the original map may not be used.
delete :: forall i k a. MaplikeClass i a => Id k -> Maplike i k a -> Maplike i k a
-- | /O(1)/. Look up the value at a key in the map.
lookUp :: forall i k a. MaplikeClass i a => Id k -> Maplike i k a -> Maybe a
lookUp {-!-}(Id a) (Maplike k) = unsafePerformIO $ do
hit k
x <- get (undefined :: i) a
return x
member :: MaplikeClass i a => Id k -> Maplike i k a -> Bool
member i m = case lookUp i m of
Just _ -> True
_ -> False
unsafeInsert :: forall i k a. MaplikeClass i a => Id k -> a -> Maplike i k a -> ()
unsafeInsert !(Id a) x !_ = unsafePerformIO $ do
set (undefined :: i) (Just x) a
return ()
-- | /O(0)/. Union of two maps.
--
-- Linearity constraints:
--
-- * After union, the component maps /may/ also be used.
--
-- * After insertion into either components, the union map may not be used.
--
-- * After insertion into the union map, the components may not be used.
infixr 2 `union`
union :: Maplike i k1 a -> Maplike i k2 a -> Maplike i (k1 :|: k2) a
-- | Unsafe equality coercion of maps.
--
-- The two maps are equal, so every link to the first map could be safely replaced by a link to the second map.
unsafeEquivalent :: Maplike i k a -> Maplike i k a -> Maplike i k a
-- | Family of finite sets of keys @('Id' k)@.
-- For efficiency reasons, use only with concrete type integers:
--
-- > Set I0 k
-- > Set I1 k
-- > Set I2 k
-- > ...
type Set i k
= Maplike (S Zero i) k ()
data S i j
-- | Helps to store a range of sets numbered from 0 to @i@-1.
-- For example, @(Sets I3 k)@ is similar to @(Set I2 k, Set I1 k, Set I0 k)@.
infixr 2 `PlusSet`
data Sets i k where
PlusSet :: Set i k -> Sets i k -> Sets (Succ i) k
-- | Helps to store a range of maps numbered from 1 to @i@.
-- For example, @(Maps1 I3 k)@ is similar to @(forall a . Map I3 k a, forall a . Map I2 k a, forall a . Map I1 k a)@.
infixr 2 `PlusMap`
data Maps i k where
PlusMap :: (forall a . Map (Succ i) k a) -> Maps i k -> Maps (Succ i) k
-- | Identifier-consuming computation. @i@ is a type-level integer.
-- A computation of type @(ICC i k a)@
-- gets @i@ maps numbered from 0 to @i@-1, an infinite list of different identifiers,
-- and returns a value of type @a@.
type ICC i k a
= Maps i k
-> (forall b . Map Zero k b)
-> [Id k]
-> a
-- | Return the value computed by an identifier-consuming computation.
-- @forall k@ ensures that the identifiers indexed by @k@ are inaccessible to the rest of the program.
runICC :: I i => (forall k . ICC i k a) -> a
-- | Identifier-consuming computation with sets. @i@ is a type-level integer.
-- A computation of type @(ICCS i k a)@
-- gets 32 sets numbered from 0 to 31, @i@ maps numbered from 1 to @i@, an infinite list of different identifiers,
-- and returns a value of type @a@.
type ICCS i k a
= Maps i k
-> Sets I32 k
-> [Id k]
-> a
-- | Return the value computed by an identifier-consuming computation with sets.
-- @forall k@ ensures that the identifiers indexed by @k@ are inaccessible to the rest of the program.
runICCS :: I i => (forall k . ICCS i k a) -> a
newId :: Int -> IO (Id k)
newId n = fmap Id $ newArray (n + 1) Nothing
newIdS :: Int -> IO (Id k)
newIdS n = fmap Id $ do
a <- newArray (n+1) Nothing
writeArray a 0 $ unsafeCoerce (0 :: Int)
return a
---------------------------------------------
---------------------------------------------
class MaplikeClass i x where
set :: i -> Maybe x -> Array (Maybe Any) -> IO ()
get :: i -> Array (Maybe Any) -> IO (Maybe x)
instance I i => MaplikeClass (M i) a where
{-# SPECIALIZE instance MaplikeClass (M I0) a #-}
{-# SPECIALIZE instance MaplikeClass (M I1) a #-}
{-# SPECIALIZE instance MaplikeClass (M I2) a #-}
set m x a = writeArray a (ind m) $ unsafeCoerce x
get m a = fmap unsafeCoerce $ readArray a (ind m)
instance (I i, I j) => MaplikeClass (S j i) () where
set m (Just _) a = do
z <- readArray' m a
writeArray' m a $ z `setBit` indS m
set m Nothing a = do
z <- readArray' m a
writeArray' m a $ z `clearBit` indS m
get m a = do
z <- readArray' m a
return $ if z `testBit` indS m then Just () else Nothing
delete !(Id a) (Maplike k) = unsafePerformIO $ do
k' <- renew "delete" k
set (undefined :: i) (Nothing :: Maybe a) a
return $ Maplike k'
-----------
ind :: forall i. I i => M i -> Int
ind _ = num (undefined :: i)
--------
indS :: forall i j. I i => S j i -> Int
indS _ = num (undefined :: i)
readArray' :: forall i j. I i => S i j -> Array (Maybe Any) -> IO Int
readArray' _ a = fmap unsafeCoerce $ readArray a $ num (undefined :: i)
writeArray' :: forall i j. I i => S i j -> Array (Maybe Any) -> Int -> IO ()
writeArray' _ a i = writeArray a (num (undefined :: i)) $ unsafeCoerce i
----------
union (Maplike k1) (Maplike k2) = unsafePerformIO $ do
k <- join k1 k2
return $ Maplike k
instance Functor (Maplike i k) where
fmap _ _ = error "fmap on Map"
instance Functor2 (Maplike i) where
fmap2 _ _ = error "fmap2 on Map"
unsafeEquivalent !_ b = b
--------------------------------
runICC = runICC'
runICC' :: forall i a . I i => (forall k . ICC i k a) -> a
runICCS = runICCS'
runICCS' :: forall i a . I i => (forall k . ICCS i k a) -> a
#ifdef __CHECK__
runICC' f = f (maps_ f) (map0_ f) $ unsafeRepeat (newId (num (undefined :: i))) f
runICCS' f = f (maps_ f) (sets_ f) $ unsafeRepeat (newIdS (num (undefined :: i))) f
#else
runICC' f = f maps map0 $ unsafeRepeat (newId (num (undefined :: i))) f
runICCS' f = f maps sets $ unsafeRepeat (newIdS (num (undefined :: i))) f
#endif
map0 :: Map Zero k a
map0 = Maplike kk
maps :: Maps i k
maps = unsafeCoerce (Maplike kk `PlusMap` maps)
sets :: Sets i k
sets = unsafeCoerce (Maplike kk `PlusSet` sets)
kk :: K
kk = unsafePerformIO create
map0_ :: x -> Map I0 k a
map0_ a = unsafePerformIO $ do
k <- create
return $ unsafeCoerce (do_nothing a `seq` Maplike k)
maps_ :: a -> Maps i k
maps_ a = unsafePerformIO $ do
k <- create
return $ unsafeCoerce (Maplike k `PlusMap` maps_ (do_nothing a))
sets_ :: a -> Sets i k
sets_ a = unsafePerformIO $ do
k <- create
return $ unsafeCoerce (Maplike k `PlusSet` sets_ (do_nothing a))
unsafeRepeat :: IO x -> a -> [x]
unsafeRepeat f g = unsafePerformIO $ do
i <- f
return (i: unsafeRepeat f (do_nothing g))
{-# NOINLINE do_nothing #-}
do_nothing :: a -> a
do_nothing i = i