sized-grid-0.1.0.0: src/SizedGrid/Coord.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module SizedGrid.Coord where
import SizedGrid.Coord.Class
import SizedGrid.Ordinal
import Control.Applicative (liftA2)
import Control.Applicative (empty)
import Control.Lens ((^.))
import Control.Monad.State
import Data.AdditiveGroup
import Data.Aeson
import Data.AffineSpace
import Data.Functor.Identity
import Data.List (intercalate)
import Data.Semigroup (Semigroup (..))
import qualified Data.Vector as V
import Generics.SOP hiding (Generic, S, Z)
import qualified Generics.SOP as SOP
import GHC.Exts (Constraint)
import GHC.Generics (Generic)
import qualified GHC.TypeLits as GHC
import System.Random (Random (..))
-- | Length of a type level list
type family Length cs where
Length '[] = 0
Length (c ': cs) = (GHC.+) 1 (Length cs)
-- | A multideminsion coordinate
newtype Coord cs = Coord {unCoord :: NP I cs}
deriving (Generic)
instance All Eq cs => Eq (Coord cs) where
Coord a == Coord b =
and $
hcollapse $ hcliftA2 (Proxy :: Proxy Eq) (\(I x) (I y) -> K (x == y)) a b
instance (All Eq cs, All Ord cs) => Ord (Coord cs) where
compare (Coord a) (Coord b) =
mconcat $
hcollapse $
hcliftA2 (Proxy :: Proxy Ord) (\(I x) (I y) -> K (compare x y)) a b
instance All Show cs => Show (Coord cs) where
show (Coord a) =
"Coord [" ++
intercalate
", "
(hcollapse $ hcliftA (Proxy :: Proxy Show) (\(I x) -> K $ show x) a) ++
"]"
instance (All ToJSON cs) => ToJSON (Coord cs) where
toJSON (Coord a) =
Array $
V.fromList $
hcollapse $ hcmap (Proxy @ToJSON) (\(I x) -> K $ toJSON x) a
instance All FromJSON cs => FromJSON (Coord cs) where
parseJSON =
withArray "Coord" $ \v ->
case SOP.fromList $ V.toList v of
Just a ->
Coord <$>
hsequence
(hcmap (Proxy @FromJSON) (\(K x) -> parseJSON x) a)
Nothing -> empty
instance All Semigroup cs => Semigroup (Coord cs) where
Coord a <> Coord b = Coord $ hcliftA2 (Proxy :: Proxy Semigroup) (liftA2 (<>)) a b
instance (All Semigroup cs, All Monoid cs) => Monoid (Coord cs) where
mappend = (<>)
mempty = Coord $ hcpure (Proxy :: Proxy Monoid) (pure mempty)
instance (All AdditiveGroup cs) => AdditiveGroup (Coord cs) where
zeroV = Coord $ hcpure (Proxy :: Proxy AdditiveGroup) (pure zeroV)
Coord a ^+^ Coord b =
Coord $ hcliftA2 (Proxy :: Proxy AdditiveGroup) (liftA2 (^+^)) a b
negateV (Coord a) =
Coord $ hcliftA (Proxy :: Proxy AdditiveGroup) (fmap negateV) a
Coord a ^-^ Coord b =
Coord $ hcliftA2 (Proxy :: Proxy AdditiveGroup) (liftA2 (^-^)) a b
instance (All Random cs) => Random (Coord cs) where
random g =
let (c, g') =
runState
(hsequence $ hcpure (Proxy :: Proxy Random) (state random))
g
in (Coord c, g')
randomR (Coord mi, Coord ma) g =
let (c, g') =
runState
(hsequence $
hcliftA2
(Proxy :: Proxy Random)
(\(I a) (I b) -> state (randomR (a, b)))
mi
ma)
g
in (Coord c, g')
-- | The type of difference between two coords. A n-dimensional coord should have a `Diff` of an n-tuple of `Integers`. We use `Identity` and our 1-tuple. Unfortuantly, each instance is manual at the moment.
type family CoordDiff (cs :: [k]) :: *
type instance CoordDiff '[] = ()
type instance CoordDiff '[a] = Identity (Diff a)
type instance CoordDiff '[a, b] = (Diff a, Diff b)
type instance CoordDiff '[a, b, c] = (Diff a, Diff b, Diff c)
type instance CoordDiff '[a, b, c, d] =
(Diff a, Diff b, Diff c, Diff d)
type instance CoordDiff '[a, b, c, d, e] =
(Diff a, Diff b, Diff c, Diff d, Diff e)
type instance CoordDiff '[a, b, c, d, e, f] =
(Diff a, Diff b, Diff c, Diff d, Diff e, Diff f)
-- | Apply `Diff` to each element of a type level list. This is required as type families can't be partially applied.
type family MapDiff xs where
MapDiff '[] = '[]
MapDiff (x ': xs) = Diff x ': MapDiff xs
instance ( All AffineSpace cs
, AdditiveGroup (CoordDiff cs)
, IsProductType (CoordDiff cs) (MapDiff cs)
) =>
AffineSpace (Coord cs) where
type Diff (Coord cs) = CoordDiff cs
Coord a .-. Coord b =
let helper ::
All AffineSpace xs => NP I xs -> NP I xs -> NP I (MapDiff xs)
helper Nil Nil = Nil
helper (I x :* xs) (I y :* ys) = I (x .-. y) :* helper xs ys
in to $ SOP $ SOP.Z $ helper a b
Coord a .+^ b =
let helper :: All AffineSpace xs => NP I xs -> NP I (MapDiff xs) -> NP I xs
helper Nil Nil = Nil
helper (I x :* xs) (I y :* ys) = I (x .+^ y) :* helper xs ys
in case from b of
SOP (SOP.Z bs) -> Coord $ helper a bs
_ -> error "Error in adding Coord. Should be unreachable"
-- | Generate all possible coords in order
allCoord ::
forall cs. (All IsCoord cs)
=> [Coord cs]
allCoord = Coord <$> hsequence (hcpure (Proxy :: Proxy IsCoord) allCoordLike)
-- | The number of elements a coord can have. This is equal to the product of the `CoordSized` of each element
type family MaxCoordSize (cs :: [k]) :: GHC.Nat where
MaxCoordSize '[] = 1
MaxCoordSize (c ': cs) = (CoordSized c) GHC.* (MaxCoordSize cs)
-- | Convert a `Coord` to its position in a vector
coordPosition :: (All IsCoord cs) => Coord cs -> Int
coordPosition (Coord a) =
let helper :: (All IsCoord xs) => NP I xs -> Integer
helper Nil = 0
helper (I c :* (cs :: NP I ys)) =
ordinalToNum (c ^. asOrdinal) * sizeOfList cs + helper cs
sizeOfList :: All IsCoord xs => NP I xs -> Integer
sizeOfList =
product .
hcollapse .
hcmap
(Proxy :: Proxy IsCoord)
(\(I (_ :: a)) -> K $ 1 + maxCoordSize (Proxy :: Proxy a))
in fromIntegral $ helper a
-- | All Diffs of the members of the list must be equal
type family AllDiffSame a xs :: Constraint where
AllDiffSame _ '[] = ()
AllDiffSame a (x ': xs) = (Diff x ~ a, AllDiffSame a xs)
-- | Calculate the Moore neighbourhood around a point. Includes the center
moorePoints ::
forall a cs. (Enum a, Num a, AllDiffSame a cs, All AffineSpace cs)
=> a
-> Coord cs
-> [Coord cs]
moorePoints n (Coord cs) =
let helper :: (All AffineSpace xs, AllDiffSame a xs) => NP I xs -> [NP I xs]
helper Nil = [Nil]
helper (I a :* as) = do
delta :: a <- [-n .. n]
next <- helper as
return (I (a .+^ delta) :* next)
in map Coord $ helper cs
-- | Calculate the von Neuman neighbourhood around a point. Includes the center
vonNeumanPoints ::
forall a cs.
( Enum a
, Num a
, Ord a
, All Integral (MapDiff cs)
, AllDiffSame a cs
, All AffineSpace cs
, Ord (CoordDiff cs)
, IsProductType (CoordDiff cs) (MapDiff cs)
, AdditiveGroup (CoordDiff cs)
)
=> a
-> Coord cs
-> [Coord cs]
vonNeumanPoints n c =
let helper :: Coord cs -> Bool
helper new =
sum
(hcollapse $
hcmap
(Proxy :: Proxy Integral)
(\(I a) -> K (abs $ fromIntegral a)) $
from (min (new .-. c) (c .-. new))) <= n
in filter helper $ moorePoints n c