{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstrainedClassMethods #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoStarIsType #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}
module Data.Finitary
( Finitary (..),
inhabitants,
inhabitantsFrom,
inhabitantsTo,
inhabitantsFromTo,
)
where
import Control.Applicative (Alternative (..), Const)
import Control.Monad (join)
import Data.Bifunctor (bimap, first)
import Data.Bool (bool)
import Data.Finitary.TH
import Data.Finite
( Finite,
finites,
separateSum,
shiftN,
weakenN,
)
import Data.Functor.Identity (Identity)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Kind (Type)
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NE
import Data.Ord (Down (..))
import Data.Proxy (Proxy (..))
import Data.Semigroup (All, Any, Dual, First, Last, Max, Min, Product, Sum)
import Data.Void (Void)
import Data.Word (Word16, Word32, Word64, Word8)
import GHC.Generics
( (:*:) (..),
(:+:) (..),
Generic,
K1 (..),
M1 (..),
Rep,
U1 (..),
V1,
from,
to,
)
import GHC.TypeNats
import Numeric.Natural (Natural)
#ifdef BITVEC
import qualified Data.Bit as B
import qualified Data.Bit.ThreadSafe as BTS
#endif
#ifdef VECTOR
import Control.Monad (forM_)
import Control.Monad.Primitive (PrimMonad (..))
import Control.Monad.ST (ST, runST)
import Data.Finite
( combineProduct,
separateProduct,
)
import Data.Type.Equality ((:~:) (..))
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Generic.Mutable as VGM
import qualified Data.Vector.Generic.Mutable.Sized as VGMS
import qualified Data.Vector.Generic.Sized as VGS
import qualified Data.Vector.Mutable.Sized as VMS
import qualified Data.Vector.Sized as VS
import qualified Data.Vector.Storable.Mutable.Sized as VSMS
import qualified Data.Vector.Storable.Sized as VSS
import qualified Data.Vector.Unboxed.Mutable.Sized as VUMS
import qualified Data.Vector.Unboxed.Sized as VUS
import Foreign.Storable (Storable)
import GHC.TypeLits.Compare (isLE)
#endif
class (Eq a, KnownNat (Cardinality a)) => Finitary (a :: Type) where
type Cardinality a :: Nat
type Cardinality a = GCardinality (Rep a)
fromFinite :: Finite (Cardinality a) -> a
default fromFinite :: (Generic a, GFinitary (Rep a), Cardinality a ~ GCardinality (Rep a)) => Finite (Cardinality a) -> a
fromFinite = to . gFromFinite
toFinite :: a -> Finite (Cardinality a)
default toFinite :: (Generic a, GFinitary (Rep a), Cardinality a ~ GCardinality (Rep a)) => a -> Finite (Cardinality a)
toFinite = gToFinite . from
start :: (1 <= Cardinality a) => a
start = fromFinite minBound
end :: (1 <= Cardinality a) => a
end = fromFinite maxBound
previous :: a -> Maybe a
previous = fmap fromFinite . guarded (/= maxBound) . dec . toFinite
next :: a -> Maybe a
next = fmap fromFinite . guarded (/= minBound) . inc . toFinite
class (KnownNat (GCardinality a)) => GFinitary (a :: Type -> Type) where
type GCardinality a :: Nat
gFromFinite :: Finite (GCardinality a) -> a x
gToFinite :: a x -> Finite (GCardinality a)
instance GFinitary V1 where
type GCardinality V1 = 0
{-# INLINE gFromFinite #-}
gFromFinite = const undefined
{-# INLINE gToFinite #-}
gToFinite = const undefined
instance GFinitary U1 where
type GCardinality U1 = 1
{-# INLINE gFromFinite #-}
gFromFinite = const U1
{-# INLINE gToFinite #-}
gToFinite = const 0
instance (Finitary a) => GFinitary (K1 _1 a) where
type GCardinality (K1 _1 a) = Cardinality a
{-# INLINE gFromFinite #-}
gFromFinite = K1 . fromFinite
{-# INLINE gToFinite #-}
gToFinite = toFinite . unK1
instance (GFinitary a, GFinitary b) => GFinitary (a :+: b) where
type GCardinality (a :+: b) = GCardinality a + GCardinality b
{-# INLINE gFromFinite #-}
gFromFinite = either (L1 . gFromFinite) (R1 . gFromFinite) . separateSum
{-# INLINABLE gToFinite #-}
gToFinite (L1 x) = weakenN . gToFinite $ x
gToFinite (R1 x) = shiftN . gToFinite $ x
instance (GFinitary a, GFinitary b) => GFinitary (a :*: b) where
type GCardinality (a :*: b) = GCardinality a * GCardinality b
{-# INLINABLE gFromFinite #-}
gFromFinite i =
let (x, y) = separateProduct' i
in gFromFinite x :*: gFromFinite y
{-# INLINABLE gToFinite #-}
gToFinite (x :*: y) = combineProduct' @(GCardinality a) @(GCardinality b) (weakenN . gToFinite $ x, weakenN . gToFinite $ y)
instance (GFinitary a) => GFinitary (M1 _x _y a) where
type GCardinality (M1 _x _y a) = GCardinality a
{-# INLINE gFromFinite #-}
gFromFinite = M1 . gFromFinite
{-# INLINE gToFinite #-}
gToFinite = gToFinite . unM1
instance Finitary Void
instance Finitary ()
instance Finitary (Proxy a)
instance Finitary Bool
instance Finitary Any
instance Finitary All
#ifdef BITVEC
instance Finitary B.Bit where
type Cardinality B.Bit = 2
{-# INLINE fromFinite #-}
fromFinite = B.Bit . toEnum . fromEnum
{-# INLINE toFinite #-}
toFinite = toEnum . fromEnum . B.unBit
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = fmap succ . guarded (== minBound)
{-# INLINE previous #-}
previous = fmap pred . guarded (== maxBound)
instance Finitary BTS.Bit where
type Cardinality BTS.Bit = 2
{-# INLINE fromFinite #-}
fromFinite = BTS.Bit . toEnum . fromEnum
{-# INLINE toFinite #-}
toFinite = toEnum . fromEnum . BTS.unBit
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = fmap succ . guarded (== minBound)
{-# INLINE previous #-}
previous = fmap pred . guarded (== maxBound)
#endif
instance Finitary Ordering
instance Finitary Char where
type Cardinality Char = $(charCardinality)
{-# INLINE fromFinite #-}
fromFinite = toEnum . fromEnum
{-# INLINE toFinite #-}
toFinite = toEnum . fromEnum
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = fmap succ . guarded (/= maxBound)
{-# INLINE previous #-}
previous = fmap pred . guarded (/= minBound)
instance Finitary Word8 where
type Cardinality Word8 = $(cardinalityOf @Word8)
{-# INLINE fromFinite #-}
fromFinite = toEnum . fromEnum
{-# INLINE toFinite #-}
toFinite = toEnum . fromEnum
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = fmap succ . guarded (/= maxBound)
{-# INLINE previous #-}
previous = fmap pred . guarded (/= minBound)
instance Finitary Word16 where
type Cardinality Word16 = $(cardinalityOf @Word16)
{-# INLINE fromFinite #-}
fromFinite = toEnum . fromEnum
{-# INLINE toFinite #-}
toFinite = toEnum . fromEnum
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = fmap succ . guarded (/= maxBound)
{-# INLINE previous #-}
previous = fmap pred . guarded (/= minBound)
instance Finitary Word32 where
type Cardinality Word32 = $(cardinalityOf @Word32)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral
{-# INLINE toFinite #-}
toFinite = fromIntegral
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = guarded (== minBound) . inc
{-# INLINE previous #-}
previous = guarded (== maxBound) . dec
instance Finitary Word64 where
type Cardinality Word64 = $(cardinalityOf @Word64)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral
{-# INLINE toFinite #-}
toFinite = fromIntegral
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = guarded (== minBound) . inc
{-# INLINE previous #-}
previous = guarded (== maxBound) . dec
instance Finitary Int8 where
type Cardinality Int8 = $(cardinalityOf @Int8)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral . subtract 128 . fromIntegral @_ @Int16
{-# INLINE toFinite #-}
toFinite = fromIntegral . (+ 128) . fromIntegral @_ @Int16
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = fmap succ . guarded (/= maxBound)
{-# INLINE previous #-}
previous = fmap pred . guarded (/= minBound)
instance Finitary Int16 where
type Cardinality Int16 = $(cardinalityOf @Int16)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral . subtract 32768 . fromIntegral @_ @Int32
{-# INLINE toFinite #-}
toFinite = fromIntegral . (+ 32768) . fromIntegral @_ @Int32
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = fmap succ . guarded (/= maxBound)
{-# INLINE previous #-}
previous = fmap pred . guarded (/= minBound)
instance Finitary Int32 where
type Cardinality Int32 = $(cardinalityOf @Int32)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral @_ @Int32 . subtract $(adjustmentOf @Int32) . fromIntegral @_ @Integer
{-# INLINE toFinite #-}
toFinite = fromIntegral . (+ $(adjustmentOf @Int32)) . fromIntegral @_ @Integer . fromEnum
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = guarded (== minBound) . inc
{-# INLINE previous #-}
previous = guarded (== maxBound) . dec
instance Finitary Int64 where
type Cardinality Int64 = $(cardinalityOf @Int64)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral @_ @Int64 . subtract $(adjustmentOf @Int64) . fromIntegral @_ @Integer
{-# INLINE toFinite #-}
toFinite = fromIntegral . (+ $(adjustmentOf @Int64)) . fromIntegral @_ @Integer . fromEnum
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = guarded (== minBound) . inc
{-# INLINE previous #-}
previous = guarded (== maxBound) . dec
instance Finitary Int where
type Cardinality Int = $(cardinalityOf @Int)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral @_ @Int . subtract $(adjustmentOf @Int) . fromIntegral @_ @Integer
{-# INLINE toFinite #-}
toFinite = fromIntegral . (+ $(adjustmentOf @Int)) . fromIntegral @_ @Integer . fromEnum
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = guarded (== minBound) . inc
{-# INLINE previous #-}
previous = guarded (== maxBound) . dec
instance Finitary Word where
type Cardinality Word = $(cardinalityOf @Word)
{-# INLINE fromFinite #-}
fromFinite = fromIntegral
{-# INLINE toFinite #-}
toFinite = fromIntegral
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = guarded (== minBound) . inc
{-# INLINE previous #-}
previous = guarded (== maxBound) . dec
instance (KnownNat n) => Finitary (Finite n) where
type Cardinality (Finite n) = n
{-# INLINE fromFinite #-}
fromFinite = id
{-# INLINE toFinite #-}
toFinite = id
{-# INLINE start #-}
start = minBound
{-# INLINE end #-}
end = maxBound
{-# INLINE next #-}
next = guarded (== minBound) . inc
{-# INLINE previous #-}
previous = guarded (== maxBound) . dec
instance (Finitary a) => Finitary (Maybe a)
instance (Finitary a, Finitary b) => Finitary (Either a b)
instance (Finitary a, Finitary b) => Finitary (a, b)
instance (Finitary a, Finitary b, Finitary c) => Finitary (a, b, c)
instance (Finitary a, Finitary b, Finitary c, Finitary d) => Finitary (a, b, c, d)
instance (Finitary a, Finitary b, Finitary c, Finitary d, Finitary e) => Finitary (a, b, c, d, e)
instance (Finitary a, Finitary b, Finitary c, Finitary d, Finitary e, Finitary f) => Finitary (a, b, c, d, e, f)
instance (Finitary a) => Finitary (Const a b)
instance (Finitary a) => Finitary (Sum a)
instance (Finitary a) => Finitary (Product a)
instance (Finitary a) => Finitary (Dual a)
instance (Finitary a) => Finitary (Last a)
instance (Finitary a) => Finitary (First a)
instance (Finitary a) => Finitary (Identity a)
instance (Finitary a) => Finitary (Max a)
instance (Finitary a) => Finitary (Min a)
instance (Finitary a) => Finitary (Down a) where
type Cardinality (Down a) = Cardinality a
{-# INLINE fromFinite #-}
fromFinite = Down . fromFinite . opp @a
{-# INLINABLE toFinite #-}
toFinite (Down x) = opp @a . toFinite $ x
#ifdef VECTOR
instance (Finitary a, KnownNat n) => Finitary (VS.Vector n a) where
type Cardinality (VS.Vector n a) = Cardinality a ^ n
{-# INLINABLE fromFinite #-}
fromFinite i = runST (go i)
where
go :: Finite (Cardinality (VS.Vector n a)) -> ST s (VS.Vector n a)
go ix = do
v <- VMS.new
unroll v ix
VS.unsafeFreeze v
{-# INLINE toFinite #-}
toFinite = roll
instance (Finitary a, VUMS.Unbox a, KnownNat n) => Finitary (VUS.Vector n a) where
type Cardinality (VUS.Vector n a) = Cardinality a ^ n
{-# INLINABLE fromFinite #-}
fromFinite i = runST (go i)
where
go :: Finite (Cardinality (VUS.Vector n a)) -> ST s (VUS.Vector n a)
go ix = do
v <- VUMS.new
unroll v ix
VUS.unsafeFreeze v
{-# INLINE toFinite #-}
toFinite = roll
instance (Finitary a, Storable a, KnownNat n) => Finitary (VSS.Vector n a) where
type Cardinality (VSS.Vector n a) = Cardinality a ^ n
{-# INLINABLE fromFinite #-}
fromFinite i = runST (go i)
where
go :: Finite (Cardinality (VSS.Vector n a)) -> ST s (VSS.Vector n a)
go ix = do
v <- VSMS.new
unroll v ix
VSS.unsafeFreeze v
{-# INLINE toFinite #-}
toFinite = roll
unroll :: forall a m v n. (Finitary a, PrimMonad m, KnownNat n, VGM.MVector v a) => VGMS.MVector v n (PrimState m) a -> Finite (Cardinality a ^ n) -> m ()
unroll v acc =
forM_ @_ @_ @_ @()
(isLE (Proxy @1) (Proxy @n))
( \Refl -> do
let (d, r) = separateProduct @(Cardinality a ^ (n -1)) @(Cardinality a) acc
let x = fromFinite r
VGMS.write v 0 x
unroll (VGMS.tail v) d
)
roll :: forall a v n. (Finitary a, VG.Vector v a, KnownNat n) => VGS.Vector v n a -> Finite (Cardinality a ^ n)
roll v = case isLE (Proxy @1) (Proxy @n) of
Nothing -> 0
Just Refl ->
let (h, t) = (VGS.head v, VGS.tail v)
in combineProduct (roll t, toFinite h)
#endif
{-# INLINE inhabitants #-}
inhabitants :: forall (a :: Type). (Finitary a) => [a]
inhabitants = fromFinite <$> finites
{-# INLINABLE inhabitantsFrom #-}
inhabitantsFrom :: forall (a :: Type). (Finitary a) => a -> NonEmpty a
inhabitantsFrom x = x :| concatMap @Maybe (fmap fromFinite . enumFrom . toFinite) (next x)
{-# INLINABLE inhabitantsTo #-}
inhabitantsTo :: forall (a :: Type). (Finitary a) => a -> NonEmpty a
inhabitantsTo x = NE.fromList (fromFinite <$> [0 .. toFinite x])
{-# INLINABLE inhabitantsFromTo #-}
inhabitantsFromTo :: forall (a :: Type). (Finitary a) => a -> a -> [a]
inhabitantsFromTo lo hi = fromFinite <$> [toFinite lo .. toFinite hi]
{-# INLINE combineProduct' #-}
combineProduct' :: forall n m. (KnownNat n, KnownNat m) => (Finite n, Finite m) -> Finite (n * m)
combineProduct' = fromIntegral . uncurry (+) . first ((natVal $ Proxy @m) *) . bimap @_ @_ @Natural @_ @Natural fromIntegral fromIntegral
{-# INLINE separateProduct' #-}
separateProduct' :: forall n m. (KnownNat n, KnownNat m) => Finite (n * m) -> (Finite n, Finite m)
separateProduct' = bimap (fromIntegral . (\x -> fromIntegral x `div` natVal @m Proxy)) (fromIntegral . (\x -> fromIntegral x `mod` natVal @m Proxy)) . join (,)
{-# INLINE inc #-}
inc :: (Num a) => a -> a
inc = (+ 1)
{-# INLINE dec #-}
dec :: (Num a) => a -> a
dec = subtract 1
{-# INLINABLE guarded #-}
guarded :: forall (a :: Type) (f :: Type -> Type). (Alternative f) => (a -> Bool) -> a -> f a
guarded p x = bool empty (pure x) (p x)
{-# INLINE opp #-}
opp :: forall a. (KnownNat (Cardinality a)) => Finite (Cardinality a) -> Finite (Cardinality a)
opp = ( maxBound - )