{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
module Generic.Data.Internal.Enum where
import GHC.Generics
import Data.Ix
gtoEnum :: (Generic a, GEnum StandardEnum (Rep a)) => Int -> a
gtoEnum = gtoEnum' @StandardEnum "gtoEnum"
gfromEnum :: (Generic a, GEnum StandardEnum (Rep a)) => a -> Int
gfromEnum = gfromEnum' @StandardEnum
genumFrom :: (Generic a, GEnum StandardEnum (Rep a)) => a -> [a]
genumFrom = genumFrom' @StandardEnum
genumFromThen :: (Generic a, GEnum StandardEnum (Rep a)) => a -> a -> [a]
genumFromThen = genumFromThen' @StandardEnum
genumFromTo :: (Generic a, GEnum StandardEnum (Rep a)) => a -> a -> [a]
genumFromTo = genumFromTo' @StandardEnum
genumFromThenTo :: (Generic a, GEnum StandardEnum (Rep a)) => a -> a -> a -> [a]
genumFromThenTo = genumFromThenTo' @StandardEnum
gtoFiniteEnum :: (Generic a, GEnum FiniteEnum (Rep a)) => Int -> a
gtoFiniteEnum = gtoEnum' @FiniteEnum "gtoFiniteEnum"
gfromFiniteEnum :: (Generic a, GEnum FiniteEnum (Rep a)) => a -> Int
gfromFiniteEnum = gfromEnum' @FiniteEnum
gfiniteEnumFrom :: (Generic a, GEnum FiniteEnum (Rep a)) => a -> [a]
gfiniteEnumFrom = genumFrom' @FiniteEnum
gfiniteEnumFromThen :: (Generic a, GEnum FiniteEnum (Rep a)) => a -> a -> [a]
gfiniteEnumFromThen = genumFromThen' @FiniteEnum
gfiniteEnumFromTo :: (Generic a, GEnum FiniteEnum (Rep a)) => a -> a -> [a]
gfiniteEnumFromTo = genumFromTo' @FiniteEnum
gfiniteEnumFromThenTo :: (Generic a, GEnum FiniteEnum (Rep a)) => a -> a -> a -> [a]
gfiniteEnumFromThenTo = genumFromThenTo' @FiniteEnum
gtoEnumRaw' :: forall opts a. (Generic a, GEnum opts (Rep a)) => Int -> a
gtoEnumRaw' = to . gToEnum @opts
gtoEnum' :: forall opts a. (Generic a, GEnum opts (Rep a)) => String -> Int -> a
gtoEnum' name n
| 0 <= n && n < card = gtoEnumRaw' @opts n
| otherwise = error $
name ++ ": out of bounds, index " ++ show n ++ ", cardinality " ++ show card
where
card = gCardinality @opts @(Rep a)
gfromEnum' :: forall opts a. (Generic a, GEnum opts (Rep a)) => a -> Int
gfromEnum' = gFromEnum @opts . from
genumMin :: Int
genumMin = 0
genumMax :: forall opts a. (Generic a, GEnum opts (Rep a)) => Int
genumMax = gCardinality @opts @(Rep a) - 1
genumFrom' :: forall opts a. (Generic a, GEnum opts (Rep a)) => a -> [a]
genumFrom' x = map toE [ i_x .. genumMax @opts @a ]
where
toE = gtoEnumRaw' @opts
i_x = gfromEnum' @opts x
genumFromThen' :: forall opts a. (Generic a, GEnum opts (Rep a)) => a -> a -> [a]
genumFromThen' x1 x2 = map toE [ i_x1, i_x2 .. bound ]
where
toE = gtoEnumRaw' @opts
i_x1 = gfromEnum' @opts x1
i_x2 = gfromEnum' @opts x2
bound | i_x1 >= i_x2 = genumMin
| otherwise = genumMax @opts @a
genumFromTo' :: forall opts a. (Generic a, GEnum opts (Rep a)) => a -> a -> [a]
genumFromTo' x y = map toE [ i_x .. i_y ]
where
toE = gtoEnumRaw' @opts
i_x = gfromEnum' @opts x
i_y = gfromEnum' @opts y
genumFromThenTo' :: forall opts a. (Generic a, GEnum opts (Rep a)) => a -> a -> a -> [a]
genumFromThenTo' x1 x2 y = map toE [ i_x1, i_x2 .. i_y ]
where
toE = gtoEnumRaw' @opts
i_x1 = gfromEnum' @opts x1
i_x2 = gfromEnum' @opts x2
i_y = gfromEnum' @opts y
gminBound :: (Generic a, GBounded (Rep a)) => a
gminBound = to gMinBound
gmaxBound :: (Generic a, GBounded (Rep a)) => a
gmaxBound = to gMaxBound
grange :: (Generic a, GIx (Rep a)) => (a, a) -> [a]
grange (m, n) = map to $ gRange (from m, from n)
gindex :: (Generic a, GIx (Rep a)) => (a, a) -> a -> Int
gindex b i
| ginRange b i = gunsafeIndex b i
| otherwise = errorWithoutStackTrace "gindex: out of bounds"
gunsafeIndex :: (Generic a, GIx (Rep a)) => (a, a) -> a -> Int
gunsafeIndex (m, n) i = gUnsafeIndex (from m, from n) (from i)
ginRange :: (Generic a, GIx (Rep a)) => (a, a) -> a -> Bool
ginRange (m, n) i = gInRange (from m, from n) (from i)
class GEnum opts f where
gCardinality :: Int
gFromEnum :: f p -> Int
gToEnum :: Int -> f p
data StandardEnum
data FiniteEnum
instance GEnum opts f => GEnum opts (M1 i c f) where
gCardinality = gCardinality @opts @f
gFromEnum = gFromEnum @opts . unM1
gToEnum = M1 . gToEnum @opts
instance (GEnum opts f, GEnum opts g) => GEnum opts (f :+: g) where
gCardinality = gCardinality @opts @f + gCardinality @opts @g
gFromEnum (L1 x) = gFromEnum @opts x
gFromEnum (R1 y) = cardF + gFromEnum @opts y
where
cardF = gCardinality @opts @f
gToEnum n
| n < cardF = L1 (gToEnum @opts n)
| otherwise = R1 (gToEnum @opts (n - cardF))
where
cardF = gCardinality @opts @f
instance (GEnum FiniteEnum f, GEnum FiniteEnum g) => GEnum FiniteEnum (f :*: g) where
gCardinality = gCardinality @FiniteEnum @f * gCardinality @FiniteEnum @g
gFromEnum (x :*: y) = gFromEnum @FiniteEnum x * cardG + gFromEnum @FiniteEnum y
where
cardG = gCardinality @FiniteEnum @g
gToEnum n = gToEnum @FiniteEnum x :*: gToEnum @FiniteEnum y
where
(x, y) = n `quotRem` cardG
cardG = gCardinality @FiniteEnum @g
instance GEnum opts U1 where
gCardinality = 1
gFromEnum U1 = 0
gToEnum _ = U1
instance (Bounded c, Enum c) => GEnum FiniteEnum (K1 i c) where
gCardinality = fromEnum (maxBound :: c) + 1
gFromEnum = fromEnum . unK1
gToEnum = K1 . toEnum
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
class GIx f where
gRange :: (f p, f p) -> [f p]
gUnsafeIndex :: (f p, f p) -> f p -> Int
gInRange :: (f p, f p) -> f p -> Bool
instance GIx f => GIx (M1 i c f) where
gRange (M1 m, M1 n) = map M1 $ gRange (m, n)
gUnsafeIndex (M1 m, M1 n) (M1 i) = gUnsafeIndex (m, n) i
gInRange (M1 m, M1 n) (M1 i) = gInRange (m, n) i
instance (GEnum StandardEnum f, GEnum StandardEnum g) => GIx (f :+: g) where
gRange (x, y) = map toE [ i_x .. i_y ]
where
toE = gToEnum @StandardEnum
i_x = gFromEnum @StandardEnum x
i_y = gFromEnum @StandardEnum y
gUnsafeIndex (m, _) i = fromIntegral (i_i - i_m)
where
i_m = gFromEnum @StandardEnum m
i_i = gFromEnum @StandardEnum i
gInRange (m, n) i = i_m <= i_i && i_i <= i_n
where
i_m = gFromEnum @StandardEnum m
i_n = gFromEnum @StandardEnum n
i_i = gFromEnum @StandardEnum i
instance (GIx f, GIx g) => GIx (f :*: g) where
gRange (m1 :*: m2, n1 :*: n2) =
[ i1 :*: i2 | i1 <- gRange (m1, n1), i2 <- gRange (m2, n2) ]
gUnsafeIndex (m1 :*: m2, n1 :*: n2) (i1 :*: i2) = int1 * rangeSize2 + int2
where
int1 = gUnsafeIndex (m1, n1) i1
int2 = gUnsafeIndex (m2, n2) i2
rangeSize2 = gUnsafeIndex (m2, n2) n2 + 1
gInRange (m1 :*: m2, n1 :*: n2) (i1 :*: i2) =
gInRange (m1, n1) i1 && gInRange (m2, n2) i2
instance GIx U1 where
gRange (U1, U1) = [U1]
gUnsafeIndex (U1, U1) U1 = 0
gInRange (U1, U1) U1 = True
instance (Ix c) => GIx (K1 i c) where
gRange (K1 m, K1 n) = map K1 $ range (m, n)
gUnsafeIndex (K1 m, K1 n) (K1 i) = index (m, n) i
gInRange (K1 m, K1 n) (K1 i) = inRange (m, n) i