{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module SizedGrid.Grid.Grid where
import SizedGrid.Coord
import SizedGrid.Coord.Class
import Control.Lens hiding (index)
import Data.Aeson
import Data.Distributive
import Data.Functor.Classes
import Data.Functor.Rep
import Data.Proxy (Proxy (..))
import qualified Data.Vector as V
import Generics.SOP
import GHC.Exts
import qualified GHC.TypeLits as GHC
newtype Grid (cs :: [*]) a = Grid
{ unGrid :: V.Vector a
} deriving (Eq, Show, Functor, Foldable, Traversable, Eq1, Show1)
instance GHC.KnownNat (MaxCoordSize cs) => Applicative (Grid cs) where
pure =
Grid .
V.replicate
(fromIntegral $ GHC.natVal (Proxy :: Proxy (MaxCoordSize cs)))
Grid fs <*> Grid as = Grid $ V.zipWith ($) fs as
instance (GHC.KnownNat (MaxCoordSize cs), All IsCoord cs) => Monad (Grid cs) where
g >>= f = imap (\p a -> f a `index` p) g
instance (GHC.KnownNat (MaxCoordSize cs), All IsCoord cs) =>
Distributive (Grid cs) where
distribute = distributeRep
instance (All IsCoord cs, GHC.KnownNat (MaxCoordSize cs)) =>
Representable (Grid cs) where
type Rep (Grid cs) = Coord cs
tabulate func = Grid $ V.fromList $ map func $ allCoord
index (Grid v) c = v V.! coordPosition c
instance (All IsCoord cs) =>
FunctorWithIndex (Coord cs) (Grid cs) where
imap func (Grid v) = Grid $ V.zipWith func (V.fromList allCoord) v
instance (All IsCoord cs) =>
FoldableWithIndex (Coord cs) (Grid cs) where
ifoldMap func (Grid v) = foldMap id $ V.zipWith func (V.fromList allCoord) v
instance (All IsCoord cs) =>
TraversableWithIndex (Coord cs) (Grid cs) where
itraverse func (Grid v) =
Grid <$> sequenceA (V.zipWith func (V.fromList allCoord) v)
type family Head xs where
Head (x ': xs) = x
-- | All but the first elements of a type level list
type family Tail xs where
Tail (x ': xs) = xs
-- | Given a grid type, give back a series of nested lists repesenting the grid. The lists will have a number of layers equal to the dimensionality.
type family CollapseGrid cs a where
CollapseGrid '[] a = a
CollapseGrid (c ': cs) a = [CollapseGrid cs a]
-- | A Constraint that all grid sizes are instances of `KnownNat`
type family AllGridSizeKnown cs :: Constraint where
AllGridSizeKnown '[] = ()
AllGridSizeKnown cs = ( GHC.KnownNat (CoordSized (Head cs))
, GHC.KnownNat (MaxCoordSize (Tail cs))
, AllGridSizeKnown (Tail cs))
-- | Convert a vector into a list of `Vector`s, where all the elements of the list have the given size.
splitVectorBySize :: Int -> V.Vector a -> [V.Vector a]
splitVectorBySize n v
| V.length v >= n = V.take n v : splitVectorBySize n (V.drop n v)
| V.null v = []
| otherwise = [v]
-- | Convert a grid to a series of nested lists. This removes type level information, but it is sometimes easier to work with lists
collapseGrid ::
forall cs a.
( SListI cs
, AllGridSizeKnown cs
)
=> Grid cs a
-> CollapseGrid cs a
collapseGrid (Grid v) =
case (shape :: Shape cs) of
ShapeNil -> v V.! 0
ShapeCons _ ->
map (collapseGrid . Grid @(Tail cs)) $
splitVectorBySize
(fromIntegral $ GHC.natVal (Proxy @(MaxCoordSize (Tail cs))))
v
-- | Convert a series of nested lists to a grid. If the size of the grid does not match the size of lists this will be `Nothing`
gridFromList ::
forall cs a. (SListI cs, AllGridSizeKnown cs)
=> CollapseGrid cs a
-> Maybe (Grid cs a)
gridFromList cg =
case (shape :: Shape cs) of
ShapeNil -> Just $ Grid $ V.singleton $ cg
ShapeCons _ ->
if length cg == fromIntegral (GHC.natVal (Proxy @(CoordSized (Head cs))))
then Grid . mconcat <$>
traverse (fmap unGrid . gridFromList @(Tail cs)) cg
else Nothing
instance (AllGridSizeKnown cs, ToJSON a, SListI cs) => ToJSON (Grid cs a) where
toJSON (Grid v) =
case (shape :: Shape cs) of
ShapeNil -> toJSON (v V.! 0)
ShapeCons _ ->
toJSON $
map (toJSON . Grid @(Tail cs)) $
splitVectorBySize
(fromIntegral $ GHC.natVal (Proxy @(MaxCoordSize (Tail cs))))
v
instance (All IsCoord cs, FromJSON a) => FromJSON (Grid cs a) where
parseJSON v = case (shape :: Shape cs) of
ShapeNil -> Grid . V.singleton <$> parseJSON v
ShapeCons _ -> do
a :: [Grid (Tail cs) a] <- parseJSON v
return $ Grid $ foldMap unGrid a