{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
module Generic.Data.Internal.Enum where
import GHC.Generics
import Data.Proxy
gtoEnum :: forall a. (Generic a, GEnum (Rep a)) => Int -> a
gtoEnum n
| 0 <= n && n < card = to (gToEnum n)
| otherwise = error $
"gtoEnum: out of bounds, index " ++ show n ++ ", card " ++ show card
where
card = gCardinality (Proxy :: Proxy (Rep a))
gfromEnum :: (Generic a, GEnum (Rep a)) => a -> Int
gfromEnum = gFromEnum . from
gminBound :: (Generic a, GBounded (Rep a)) => a
gminBound = to gMinBound
gmaxBound :: (Generic a, GBounded (Rep a)) => a
gmaxBound = to gMaxBound
class GEnum f where
gCardinality :: proxy f -> Int
gFromEnum :: f p -> Int
gToEnum :: Int -> f p
instance GEnum f => GEnum (M1 i c f) where
gCardinality _ = gCardinality (Proxy :: Proxy f)
gFromEnum = gFromEnum . unM1
gToEnum = M1 . gToEnum
instance (GEnum f, GEnum g) => GEnum (f :+: g) where
gCardinality _ = gCardinality (Proxy :: Proxy f) + gCardinality (Proxy :: Proxy g)
gFromEnum (L1 x) = gFromEnum x
gFromEnum (R1 y) = cardF + gFromEnum y
where
cardF = gCardinality (Proxy :: Proxy f)
gToEnum n
| n < cardF = L1 (gToEnum n)
| otherwise = R1 (gToEnum (n - cardF))
where
cardF = gCardinality (Proxy :: Proxy f)
instance GEnum U1 where
gCardinality _ = 1
gFromEnum U1 = 0
gToEnum _ = U1
class GBounded f where
gMinBound :: f p
gMaxBound :: f p
deriving instance GBounded f => GBounded (M1 i c f)
instance GBounded U1 where
gMinBound = U1
gMaxBound = U1
instance Bounded c => GBounded (K1 i c) where
gMinBound = K1 minBound
gMaxBound = K1 maxBound
instance (GBounded f, GBounded g) => GBounded (f :+: g) where
gMinBound = L1 gMinBound
gMaxBound = R1 gMaxBound
instance (GBounded f, GBounded g) => GBounded (f :*: g) where
gMinBound = gMinBound :*: gMinBound
gMaxBound = gMaxBound :*: gMaxBound