packages feed

grids 0.2.0.0 → 0.3.0.0

raw patch · 19 files changed

+1042/−231 lines, 19 filesdep +comonaddep +deepseqdep +gaugedep −finite-typelitsPVP ok

version bump matches the API change (PVP)

Dependencies added: comonad, deepseq, gauge, grids, hspec, singletons

Dependencies removed: finite-typelits

API changes (from Hackage documentation)

- Data.Grid: (:#) :: x -> y -> (:#) x y
- Data.Grid: data x (:#) y
- Data.Grid: fromCoord :: Dimensions dims => Proxy dims -> Coord dims -> Finite (GridSize dims)
- Data.Grid: instance (Data.Grid.Dimensions dims, GHC.Base.Monoid a) => GHC.Base.Monoid (Data.Grid.Grid dims a)
- Data.Grid: instance (Data.Grid.Dimensions dims, GHC.Base.Semigroup a) => GHC.Base.Semigroup (Data.Grid.Grid dims a)
- Data.Grid: instance (Data.Grid.Dimensions dims, GHC.Show.Show (Data.Grid.NestedLists dims a)) => GHC.Show.Show (Data.Grid.Grid dims a)
- Data.Grid: instance (GHC.Classes.Eq x, GHC.Classes.Eq y) => GHC.Classes.Eq (x Data.Grid.:# y)
- Data.Grid: instance (GHC.Classes.Ord x, GHC.Classes.Ord y) => GHC.Classes.Ord (x Data.Grid.:# y)
- Data.Grid: instance (GHC.Show.Show x, GHC.Show.Show y) => GHC.Show.Show (x Data.Grid.:# y)
- Data.Grid: instance (GHC.TypeNats.KnownNat (Data.Grid.GridSize (x : y : xs)), GHC.TypeNats.KnownNat x, Data.Grid.Dimensions (y : xs)) => Data.Grid.Dimensions (x : y : xs)
- Data.Grid: instance Data.Foldable.Foldable (Data.Grid.Grid dims)
- Data.Grid: instance Data.Grid.Dimensions dims => Data.Distributive.Distributive (Data.Grid.Grid dims)
- Data.Grid: instance Data.Grid.Dimensions dims => Data.Functor.Rep.Representable (Data.Grid.Grid dims)
- Data.Grid: instance Data.Grid.Dimensions dims => GHC.Base.Applicative (Data.Grid.Grid dims)
- Data.Grid: instance Data.Traversable.Traversable (Data.Grid.Grid dims)
- Data.Grid: instance GHC.Base.Functor (Data.Grid.Grid dims)
- Data.Grid: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Grid.Grid dims a)
- Data.Grid: instance GHC.TypeNats.KnownNat x => Data.Grid.Dimensions '[x]
- Data.Grid: nestLists :: Dimensions dims => Proxy dims -> Vector a -> NestedLists dims a
- Data.Grid: toCoord :: Dimensions dims => Proxy dims -> Finite (GridSize dims) -> Coord dims
- Data.Grid: unNestLists :: Dimensions dims => Proxy dims -> NestedLists dims a -> [a]
- Data.Grid.Lens: cell :: forall dims a. (Dimensions dims, Eq (Coord dims)) => Coord dims -> Lens' (Grid dims a) a
+ Data.Grid: Coord :: [Int] -> Coord
+ Data.Grid: [toVector] :: Grid a -> Vector a
+ Data.Grid: [unCoord] :: Coord -> [Int]
+ Data.Grid: appendC :: Coord ns -> Coord ms -> Coord (ns ++ ms)
+ Data.Grid: autoConvolute :: forall window dims f a b. (Dimensions dims, Dimensions window, Functor f, Neighboring window) => (Grid window (Coord dims) -> f (Coord dims)) -> (f a -> b) -> Grid dims a -> Grid dims b
+ Data.Grid: cell :: forall ind dims a. Dimensions dims => Coord dims -> Lens' (Grid dims a) a
+ Data.Grid: clampWindow :: Dimensions dims => Grid window (Coord dims) -> Grid window (Coord dims)
+ Data.Grid: class Neighboring dims
+ Data.Grid: convolute :: forall dims f a b. (Functor f, Dimensions dims) => (Coord dims -> f (Coord dims)) -> (f a -> b) -> Grid dims a -> Grid dims b
+ Data.Grid: coord :: forall dims. SingI dims => [Int] -> Maybe (Coord dims)
+ Data.Grid: fromList' :: forall dims a. SingI dims => [a] -> Grid dims a
+ Data.Grid: fromNestedLists' :: forall dims a. Dimensions dims => NestedLists dims a -> Grid dims a
+ Data.Grid: index :: Representable f => f a -> Rep f -> a
+ Data.Grid: joinGrid :: Grid dims (Grid ns a) -> Grid (dims ++ ns) a
+ Data.Grid: newtype Coord (dims :: [Nat])
+ Data.Grid: permute :: forall (key :: [Nat]) from a invertedKey. (SingI invertedKey, invertedKey ~ InvertKey (EnumFromTo 0 (Length from - 1)) key, ValidPermutation key from, Dimensions from, Dimensions (Permuted key from)) => Grid from a -> Grid (Permuted key from) a
+ Data.Grid: permuteCoord :: forall (key :: [Nat]) to from. SingI key => Coord from -> Coord to
+ Data.Grid: safeWindow :: Dimensions dims => Grid window (Coord dims) -> Compose (Grid window) Maybe (Coord dims)
+ Data.Grid: splitGrid :: forall outer inner a from. (from ~ (outer ++ inner), Dimensions from, Dimensions inner, Dimensions outer, NestedLists from a ~ NestedLists outer (NestedLists inner a)) => Grid from a -> Grid outer (Grid inner a)
+ Data.Grid: tabulate :: Representable f => (Rep f -> a) -> f a
+ Data.Grid: transpose :: (KnownNat x, KnownNat y) => Grid '[x, y] a -> Grid '[y, x] a
+ Data.Grid: type ValidPermutation key from = (Sort key == EnumFromTo 0 (Length from - 1)) ?! (Text "Malformed permutation hint: " :<>: ShowType key :$$: Text "When permuting matrix of size: " :<>: ShowType from :$$: Text "Key must be a permutation of " :<>: ShowType (EnumFromTo 0 (Length from - 1)) :$$: Text "e.g. the identity permutation for 2x2 is @[0, 1]" :$$: Text "e.g. matrix transpose for 2x2 is @[1, 0]")
+ Data.Grid: type family Permuted (key :: [Nat]) (from :: [Nat]) :: [Nat]
+ Data.Grid: unconsC :: Coord (n : ns) -> (Int, Coord ns)
+ Data.Grid: wrapWindow :: Dimensions dims => Grid window (Coord dims) -> Grid window (Coord dims)
+ Data.Grid.Examples.Conway: glider :: [Coord '[10, 10]]
+ Data.Grid.Examples.Conway: rule :: Grid '[3, 3] Bool -> Bool
+ Data.Grid.Examples.Conway: showBool :: Bool -> Char
+ Data.Grid.Examples.Conway: showGrid :: Dimensions '[x, y] => Grid '[x, y] Bool -> String
+ Data.Grid.Examples.Conway: simulate :: Int -> Grid '[10, 10] Bool
+ Data.Grid.Examples.Conway: start :: Grid '[10, 10] Bool
+ Data.Grid.Examples.Conway: step :: Dimensions dims => Grid dims Bool -> Grid dims Bool
+ Data.Grid.Examples.Intro: avg :: Foldable f => f Int -> Int
+ Data.Grid.Examples.Intro: big :: Grid '[5, 5, 5, 5] Int
+ Data.Grid.Examples.Intro: clampGauss :: Dimensions dims => Grid dims Double -> Grid dims Double
+ Data.Grid.Examples.Intro: coordGrid :: Grid '[5, 5] (Coord '[5, 5])
+ Data.Grid.Examples.Intro: coords :: Grid '[3, 3] (Coord '[3, 3])
+ Data.Grid.Examples.Intro: doubleGrid :: Grid '[3, 3] Double
+ Data.Grid.Examples.Intro: gauss :: Dimensions dims => Grid dims Double -> Grid dims Double
+ Data.Grid.Examples.Intro: med :: Grid '[3, 3, 3] Int
+ Data.Grid.Examples.Intro: mx :: Foldable f => f Int -> Int
+ Data.Grid.Examples.Intro: pacmanGauss :: Dimensions dims => Grid dims Double -> Grid dims Double
+ Data.Grid.Examples.Intro: seeNeighboring :: Grid '[3, 3] a -> Grid '[3, 3] (Grid '[3, 3] (Maybe a))
+ Data.Grid.Examples.Intro: simpleGauss :: Grid '[3, 3] Double
+ Data.Grid.Examples.Intro: simpleGrid :: Grid '[5, 5] Int
+ Data.Grid.Examples.Intro: small :: Grid '[3, 3] Int
+ Data.Grid.Examples.Intro: small' :: Grid '[5, 5] Int
+ Data.Grid.Internal.Convolution: autoConvolute :: forall window dims f a b. (Dimensions dims, Dimensions window, Functor f, Neighboring window) => (Grid window (Coord dims) -> f (Coord dims)) -> (f a -> b) -> Grid dims a -> Grid dims b
+ Data.Grid.Internal.Convolution: clampWindow :: Dimensions dims => Grid window (Coord dims) -> Grid window (Coord dims)
+ Data.Grid.Internal.Convolution: class Neighboring dims
+ Data.Grid.Internal.Convolution: convolute :: forall dims f a b. (Functor f, Dimensions dims) => (Coord dims -> f (Coord dims)) -> (f a -> b) -> Grid dims a -> Grid dims b
+ Data.Grid.Internal.Convolution: criticalError :: a
+ Data.Grid.Internal.Convolution: instance (GHC.TypeNats.KnownNat n, Data.Grid.Internal.Convolution.Neighboring ns) => Data.Grid.Internal.Convolution.Neighboring (n : ns)
+ Data.Grid.Internal.Convolution: instance GHC.TypeNats.KnownNat n => Data.Grid.Internal.Convolution.Neighboring '[n]
+ Data.Grid.Internal.Convolution: neighboring :: (Dimensions dims, Neighboring dims) => Coord dims -> Grid dims (Coord dims)
+ Data.Grid.Internal.Convolution: neighbors :: Neighboring dims => Grid dims (Coord dims)
+ Data.Grid.Internal.Convolution: safeWindow :: Dimensions dims => Grid window (Coord dims) -> Compose (Grid window) Maybe (Coord dims)
+ Data.Grid.Internal.Convolution: window :: forall window dims. (Neighboring window, Dimensions window) => Coord dims -> Grid window (Coord dims)
+ Data.Grid.Internal.Convolution: wrapWindow :: Dimensions dims => Grid window (Coord dims) -> Grid window (Coord dims)
+ Data.Grid.Internal.Coord: Coord :: [Int] -> Coord
+ Data.Grid.Internal.Coord: [unCoord] :: Coord -> [Int]
+ Data.Grid.Internal.Coord: appendC :: Coord ns -> Coord ms -> Coord (ns ++ ms)
+ Data.Grid.Internal.Coord: clamp :: Int -> Int -> Int -> Int
+ Data.Grid.Internal.Coord: clampCoord :: forall dims. SingI dims => Coord dims -> Coord dims
+ Data.Grid.Internal.Coord: coerceCoordDims :: Coord ns -> Coord ms
+ Data.Grid.Internal.Coord: coord :: forall dims. SingI dims => [Int] -> Maybe (Coord dims)
+ Data.Grid.Internal.Coord: coordInBounds :: forall ns. SingI ns => Coord ns -> Bool
+ Data.Grid.Internal.Coord: gridSize :: forall (dims :: [Nat]). SingI dims => Int
+ Data.Grid.Internal.Coord: highestIndex :: forall n. KnownNat n => Int
+ Data.Grid.Internal.Coord: instance (GHC.TypeNats.KnownNat n, GHC.Enum.Bounded (Data.Grid.Internal.Coord.Coord ns)) => GHC.Enum.Bounded (Data.Grid.Internal.Coord.Coord (n : ns))
+ Data.Grid.Internal.Coord: instance (GHC.TypeNats.KnownNat x, GHC.TypeNats.KnownNat y, Data.Singletons.Internal.SingI rest, GHC.Enum.Bounded (Data.Grid.Internal.Coord.Coord rest), GHC.Enum.Enum (Data.Grid.Internal.Coord.Coord (y : rest))) => GHC.Enum.Enum (Data.Grid.Internal.Coord.Coord (x : y : rest))
+ Data.Grid.Internal.Coord: instance GHC.Classes.Eq (Data.Grid.Internal.Coord.Coord dims)
+ Data.Grid.Internal.Coord: instance GHC.Enum.Bounded (Data.Grid.Internal.Coord.Coord '[])
+ Data.Grid.Internal.Coord: instance GHC.Enum.Enum (Data.Grid.Internal.Coord.Coord ns) => GHC.Num.Num (Data.Grid.Internal.Coord.Coord ns)
+ Data.Grid.Internal.Coord: instance GHC.Exts.IsList (Data.Grid.Internal.Coord.Coord dims)
+ Data.Grid.Internal.Coord: instance GHC.Show.Show (Data.Grid.Internal.Coord.Coord dims)
+ Data.Grid.Internal.Coord: instance GHC.TypeNats.KnownNat n => GHC.Enum.Enum (Data.Grid.Internal.Coord.Coord '[n])
+ Data.Grid.Internal.Coord: newtype Coord (dims :: [Nat])
+ Data.Grid.Internal.Coord: pattern (:#) :: Int -> Coord ns -> Coord (n : ns)
+ Data.Grid.Internal.Coord: unconsC :: Coord (n : ns) -> (Int, Coord ns)
+ Data.Grid.Internal.Coord: wrapCoord :: forall dims. SingI dims => Coord dims -> Coord dims
+ Data.Grid.Internal.Errors: infixr 1 ?!
+ Data.Grid.Internal.Errors: type family (b :: Bool) ?! (e :: ErrorMessage) :: Constraint
+ Data.Grid.Internal.Grid: (//) :: forall dims a. Enum (Coord dims) => Grid dims a -> [(Coord dims, a)] -> Grid dims a
+ Data.Grid.Internal.Grid: Grid :: Vector a -> Grid a
+ Data.Grid.Internal.Grid: [toVector] :: Grid a -> Vector a
+ Data.Grid.Internal.Grid: class (AllC KnownNat dims, SingI dims, Enum (Coord dims), Bounded (Coord dims)) => Dimensions (dims :: [Nat])
+ Data.Grid.Internal.Grid: data Coord (dims :: [Nat])
+ Data.Grid.Internal.Grid: fromList :: forall dims a. SingI dims => [a] -> Maybe (Grid dims a)
+ Data.Grid.Internal.Grid: fromList' :: forall dims a. SingI dims => [a] -> Grid dims a
+ Data.Grid.Internal.Grid: fromNestedLists :: forall dims a. Dimensions dims => NestedLists dims a -> Maybe (Grid dims a)
+ Data.Grid.Internal.Grid: fromNestedLists' :: forall dims a. Dimensions dims => NestedLists dims a -> Grid dims a
+ Data.Grid.Internal.Grid: generate :: forall dims a. SingI dims => (Int -> a) -> Grid dims a
+ Data.Grid.Internal.Grid: instance (Data.Grid.Internal.NestedLists.Dimensions dims, GHC.Base.Monoid a) => GHC.Base.Monoid (Data.Grid.Internal.Grid.Grid dims a)
+ Data.Grid.Internal.Grid: instance (Data.Grid.Internal.NestedLists.Dimensions dims, GHC.Base.Semigroup a) => GHC.Base.Semigroup (Data.Grid.Internal.Grid.Grid dims a)
+ Data.Grid.Internal.Grid: instance (Data.Grid.Internal.Pretty.PrettyList (Data.Grid.Internal.NestedLists.NestedLists dims a), Data.Grid.Internal.NestedLists.Dimensions dims, GHC.Show.Show (Data.Grid.Internal.NestedLists.NestedLists dims a)) => GHC.Show.Show (Data.Grid.Internal.Grid.Grid dims a)
+ Data.Grid.Internal.Grid: instance (GHC.Num.Num n, Data.Grid.Internal.NestedLists.Dimensions dims) => GHC.Num.Num (Data.Grid.Internal.Grid.Grid dims n)
+ Data.Grid.Internal.Grid: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Data.Grid.Internal.Grid.Grid dims a)
+ Data.Grid.Internal.Grid: instance Data.Foldable.Foldable (Data.Grid.Internal.Grid.Grid dims)
+ Data.Grid.Internal.Grid: instance Data.Grid.Internal.NestedLists.Dimensions dims => Data.Distributive.Distributive (Data.Grid.Internal.Grid.Grid dims)
+ Data.Grid.Internal.Grid: instance Data.Grid.Internal.NestedLists.Dimensions dims => Data.Functor.Rep.Representable (Data.Grid.Internal.Grid.Grid dims)
+ Data.Grid.Internal.Grid: instance Data.Grid.Internal.NestedLists.Dimensions dims => GHC.Base.Applicative (Data.Grid.Internal.Grid.Grid dims)
+ Data.Grid.Internal.Grid: instance Data.Traversable.Traversable (Data.Grid.Internal.Grid.Grid dims)
+ Data.Grid.Internal.Grid: instance GHC.Base.Functor (Data.Grid.Internal.Grid.Grid dims)
+ Data.Grid.Internal.Grid: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Grid.Internal.Grid.Grid dims a)
+ Data.Grid.Internal.Grid: nestLists :: Dimensions dims => Proxy dims -> Vector a -> NestedLists dims a
+ Data.Grid.Internal.Grid: newtype Grid (dims :: [Nat]) a
+ Data.Grid.Internal.Grid: toNestedLists :: forall dims a. Dimensions dims => Grid dims a -> NestedLists dims a
+ Data.Grid.Internal.Grid: type family NestedLists (dims :: [Nat]) a
+ Data.Grid.Internal.Grid: unNestLists :: Dimensions dims => Proxy dims -> NestedLists dims a -> [a]
+ Data.Grid.Internal.Lens: cell :: forall ind dims a. Dimensions dims => Coord dims -> Lens' (Grid dims a) a
+ Data.Grid.Internal.Lens: lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
+ Data.Grid.Internal.Lens: type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
+ Data.Grid.Internal.Lens: type Lens' s a = Lens s s a a
+ Data.Grid.Internal.Nest: joinGrid :: Grid dims (Grid ns a) -> Grid (dims ++ ns) a
+ Data.Grid.Internal.Nest: splitGrid :: forall outer inner a from. (from ~ (outer ++ inner), Dimensions from, Dimensions inner, Dimensions outer, NestedLists from a ~ NestedLists outer (NestedLists inner a)) => Grid from a -> Grid outer (Grid inner a)
+ Data.Grid.Internal.NestedLists: chunkVector :: forall a. Int -> Vector a -> [Vector a]
+ Data.Grid.Internal.NestedLists: class (AllC KnownNat dims, SingI dims, Enum (Coord dims), Bounded (Coord dims)) => Dimensions (dims :: [Nat])
+ Data.Grid.Internal.NestedLists: instance (GHC.TypeNats.KnownNat x, GHC.Enum.Bounded (Data.Grid.Internal.Coord.Coord xs), Data.Singletons.Internal.SingI xs, Data.Grid.Internal.NestedLists.Dimensions (y : xs)) => Data.Grid.Internal.NestedLists.Dimensions (x : y : xs)
+ Data.Grid.Internal.NestedLists: instance GHC.TypeNats.KnownNat x => Data.Grid.Internal.NestedLists.Dimensions '[x]
+ Data.Grid.Internal.NestedLists: nestLists :: Dimensions dims => Proxy dims -> Vector a -> NestedLists dims a
+ Data.Grid.Internal.NestedLists: type family NestedLists (dims :: [Nat]) a
+ Data.Grid.Internal.NestedLists: unNestLists :: Dimensions dims => Proxy dims -> NestedLists dims a -> [a]
+ Data.Grid.Internal.Pretty: class PrettyList l
+ Data.Grid.Internal.Pretty: instance GHC.Show.Show a => Data.Grid.Internal.Pretty.PrettyList [[[a]]]
+ Data.Grid.Internal.Pretty: instance GHC.Show.Show a => Data.Grid.Internal.Pretty.PrettyList [[a]]
+ Data.Grid.Internal.Pretty: instance GHC.Show.Show a => Data.Grid.Internal.Pretty.PrettyList [a]
+ Data.Grid.Internal.Pretty: prettyList :: PrettyList l => l -> String
+ Data.Grid.Internal.Transpose: permute :: forall (key :: [Nat]) from a invertedKey. (SingI invertedKey, invertedKey ~ InvertKey (EnumFromTo 0 (Length from - 1)) key, ValidPermutation key from, Dimensions from, Dimensions (Permuted key from)) => Grid from a -> Grid (Permuted key from) a
+ Data.Grid.Internal.Transpose: permuteCoord :: forall (key :: [Nat]) to from. SingI key => Coord from -> Coord to
+ Data.Grid.Internal.Transpose: transpose :: (KnownNat x, KnownNat y) => Grid '[x, y] a -> Grid '[y, x] a
+ Data.Grid.Internal.Transpose: type ValidPermutation key from = (Sort key == EnumFromTo 0 (Length from - 1)) ?! (Text "Malformed permutation hint: " :<>: ShowType key :$$: Text "When permuting matrix of size: " :<>: ShowType from :$$: Text "Key must be a permutation of " :<>: ShowType (EnumFromTo 0 (Length from - 1)) :$$: Text "e.g. the identity permutation for 2x2 is @[0, 1]" :$$: Text "e.g. matrix transpose for 2x2 is @[1, 0]")
+ Data.Grid.Internal.Transpose: type family InvertKey ref key :: [Nat]
- Data.Grid: (//) :: forall dims a. (Dimensions dims) => Grid dims a -> [(Coord dims, a)] -> Grid dims a
+ Data.Grid: (//) :: forall dims a. Enum (Coord dims) => Grid dims a -> [(Coord dims, a)] -> Grid dims a
- Data.Grid: Grid :: (Vector a) -> Grid a
+ Data.Grid: Grid :: Vector a -> Grid a
- Data.Grid: class (AllC KnownNat dims, KnownNat (GridSize dims)) => Dimensions (dims :: [Nat])
+ Data.Grid: class (AllC KnownNat dims, SingI dims, Enum (Coord dims), Bounded (Coord dims)) => Dimensions (dims :: [Nat])
- Data.Grid: fromList :: forall a dims. (KnownNat (GridSize dims), Dimensions dims) => [a] -> Maybe (Grid dims a)
+ Data.Grid: fromList :: forall dims a. SingI dims => [a] -> Maybe (Grid dims a)
- Data.Grid: generate :: forall dims a. Dimensions dims => (Int -> a) -> Grid dims a
+ Data.Grid: generate :: forall dims a. SingI dims => (Int -> a) -> Grid dims a
- Data.Grid: gridSize :: Dimensions dims => Proxy dims -> Int
+ Data.Grid: gridSize :: forall (dims :: [Nat]). SingI dims => Int
- Data.Grid: toNestedLists :: forall dims a. (Dimensions dims) => Grid dims a -> NestedLists dims a
+ Data.Grid: toNestedLists :: forall dims a. Dimensions dims => Grid dims a -> NestedLists dims a

Files

ChangeLog.md view
@@ -1,3 +1,12 @@ # Changelog for grids +## 0.3.0.0+- Huge changes to Grid types, Coord types.+- Add Convolution combinators+- Add Permutation combinators++## 0.2.0.0+- Add docs+- Initial release+ ## Unreleased changes
README.md view
@@ -3,14 +3,26 @@ [HACKAGE](http://hackage.haskell.org/package/grids)  Grids can have an arbitrary amount of dimensions, specified by a type-level-list of `Nat`s. They're backed by a single contiguous Vector and gain the+list of `Nat`s.++Each grid has Functor, Applicative, and Representable instances making it easy to do **Matlab-style** matrix programming. `liftA2 (+)` does piecewise addition, etc.++By combining with `Control.Comonad.Representable.Store` you can do context-wise **linear transformations** for things like **Image Processing** or **Cellular Automata**.++All in a typesafe package!++Still working out the best interface for this stuff, feedback is appreciated!++Grids backed by a single contiguous Vector and gain the associated performance benefits. Currently only boxed immutable vectors are supported, but let me know if you need other variants. -Here's how we might represent a Tic-Tac-Toe board:+Here's how we might represent a Tic-Tac-Toe board which we'll fill with+alternating X's and O's:  ```haskell data Piece = X | O deriving Show+toPiece :: Int -> Piece toPiece n = if even n then X                       else O 
+ benchmarks/Benchmarks.hs view
@@ -0,0 +1,15 @@+module Benchmarks where++import Gauge.Main+import Data.Grid as G++benchTranspose :: forall dims a x y. (Dimensions dims) =>  (dims ~ [x, y]) => Int -> (Grid dims Int, Grid dims Int, Grid dims Int)+benchTranspose a = (transpose $ pure 1, transpose $ pure 2, transpose $ pure 3)++main :: IO ()+main = defaultMain+  [ bgroup+      "permutations"+      [ bench "nf [150, 150]" $ nf (benchTranspose @[200, 200]) 2+      ]+  ]
grids.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 21db274f8abd8e6d956d24ef25c4ad5c0c1118727eccae42c59a1db28229edd3+-- hash: b776a0436dd05eb78d384efe21494a96d7c6b28f0de9046d2011ea75f53025df  name:           grids-version:        0.2.0.0+version:        0.3.0.0 description:    Arbitrary sized type-safe grids with useful combinators category:       Data Structures homepage:       https://github.com/ChrisPenner/grids#readme@@ -29,15 +29,72 @@ library   exposed-modules:       Data.Grid-      Data.Grid.Lens+      Data.Grid.Examples.Conway+      Data.Grid.Examples.Intro+      Data.Grid.Internal.Convolution+      Data.Grid.Internal.Coord+      Data.Grid.Internal.Errors+      Data.Grid.Internal.Grid+      Data.Grid.Internal.Identity+      Data.Grid.Internal.Lens+      Data.Grid.Internal.Nest+      Data.Grid.Internal.NestedLists+      Data.Grid.Internal.Pretty+      Data.Grid.Internal.Transpose   other-modules:       Paths_grids   hs-source-dirs:       src+  default-extensions: KindSignatures PolyKinds TypeApplications ScopedTypeVariables TypeOperators TypeFamilies FlexibleInstances FlexibleContexts MultiParamTypeClasses DataKinds GeneralizedNewtypeDeriving DeriveTraversable DeriveFunctor ConstraintKinds+  ghc-options: -fwarn-redundant-constraints   build-depends:       adjunctions     , base >=4.7 && <5+    , comonad+    , deepseq     , distributive-    , finite-typelits+    , singletons+    , vector+  default-language: Haskell2010++test-suite specs+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_grids+  hs-source-dirs:+      test+  default-extensions: KindSignatures PolyKinds TypeApplications ScopedTypeVariables TypeOperators TypeFamilies FlexibleInstances FlexibleContexts MultiParamTypeClasses DataKinds GeneralizedNewtypeDeriving DeriveTraversable DeriveFunctor ConstraintKinds+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -main-is Spec+  build-depends:+      adjunctions+    , base >=4.7 && <5+    , comonad+    , deepseq+    , distributive+    , grids+    , hspec+    , singletons+    , vector+  default-language: Haskell2010++benchmark stat+  type: exitcode-stdio-1.0+  main-is: Benchmarks.hs+  other-modules:+      Paths_grids+  hs-source-dirs:+      benchmarks+  default-extensions: KindSignatures PolyKinds TypeApplications ScopedTypeVariables TypeOperators TypeFamilies FlexibleInstances FlexibleContexts MultiParamTypeClasses DataKinds GeneralizedNewtypeDeriving DeriveTraversable DeriveFunctor ConstraintKinds+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -main-is Benchmarks+  build-depends:+      adjunctions+    , base >=4.7 && <5+    , comonad+    , deepseq+    , distributive+    , gauge+    , grids+    , singletons     , vector   default-language: Haskell2010
src/Data/Grid.hs view
@@ -1,212 +1,65 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# language ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE TypeInType #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE InstanceSigs #-}-{-# LANGUAGE UndecidableSuperClasses #-}-{-# LANGUAGE RankNTypes #-}- module Data.Grid-  ( Grid(..)-  , GridSize-  , Dimensions(..)-  , Coord-  , (:#)(..)-  , NestedLists+  (+  -- * Grids+   Grid(..)+   -- * Creation   , generate-  , toNestedLists+  , Rep.tabulate   , fromNestedLists+  , fromNestedLists'   , fromList-  , (//)-  )-where--import           Data.Distributive-import           Data.Functor.Rep-import qualified Data.Vector                   as V-import           Data.Proxy-import           Data.Kind-import           GHC.TypeNats                  as N-import           Data.Finite-import           Control.Applicative-import           Data.List-import           Data.Bifunctor--toFinite :: (KnownNat n) => Integral m => m -> Finite n-toFinite = finite . fromIntegral--fromFinite :: Num n => Finite m -> n-fromFinite = fromIntegral . getFinite---- | An grid of arbitrary dimensions.------ e.g. a @Grid [2, 3] Int@ might look like:------ > generate id :: Grid [2, 3] Int--- > (Grid [[0,1,2],--- >        [3,4,5]])-newtype Grid (dims :: [Nat]) a =-  Grid  (V.Vector a)-  deriving (Eq, Functor, Foldable, Traversable)--instance (Dimensions dims, Show (NestedLists dims a)) => Show (Grid dims a) where-  show g = "(Grid " ++ show (toNestedLists g) ++ ")"--instance (Dimensions dims, Semigroup a) => Semigroup (Grid dims a) where-  (<>) = liftA2 (<>)--instance (Dimensions dims, Monoid a) => Monoid (Grid dims a) where-  mempty = pure mempty--instance (Dimensions dims) => Applicative (Grid dims) where-  pure a = tabulate (const a)-  liftA2 f (Grid v) (Grid u) = Grid $ V.zipWith f v u---- | Calculate the number of elements in a grid of the given dimensionality-type family GridSize (dims :: [Nat]) :: Nat where-  GridSize '[] = 0-  GridSize (x:'[]) = x-  GridSize (x:xs) = (x N.* GridSize xs)---- | Used for constructing arbitrary depth coordinate lists --- e.g. @('Finite' 2 ':#' 'Finite' 3)@-data x :# y = x :# y-  deriving (Show, Eq, Ord)--infixr 9 :#---- | The coordinate type for a given dimensionality------ > Coord [2, 3] == Finite 2 :# Finite 3--- > Coord [4, 3, 2] == Finite 4 :# Finite 3 :# Finite 2-type family Coord (dims :: [Nat]) where-  Coord '[n] = Finite n-  Coord (n:xs) = Finite n :# Coord xs---- | Represents valid dimensionalities. All non empty lists of Nats have--- instances-class (AllC KnownNat dims, KnownNat (GridSize dims)) => Dimensions (dims :: [Nat]) where-  toCoord :: Proxy dims -> Finite (GridSize dims) -> Coord dims-  fromCoord :: Proxy dims -> Coord dims -> Finite (GridSize dims)-  gridSize-    :: Proxy dims -> Int-  gridSize _ = fromIntegral $ natVal (Proxy @(GridSize dims))-  nestLists :: Proxy dims -> V.Vector a -> NestedLists dims a-  unNestLists :: Proxy dims -> NestedLists dims a -> [a]--type family AllC (c :: x -> Constraint) (ts :: [x]) :: Constraint where-  AllC c '[] = ()-  AllC c (x:xs) = (c x, AllC c xs)--instance (KnownNat x) => Dimensions '[x] where-  toCoord _ i = i-  fromCoord _ i = i-  nestLists _ = V.toList-  unNestLists _ xs = xs+  , fromList' -instance (KnownNat (GridSize (x:y:xs)), KnownNat x, Dimensions (y:xs)) => Dimensions (x:y:xs) where-  toCoord _ n = firstCoord :# toCoord (Proxy @(y:xs)) remainder-    where-      firstCoord = toFinite (n `div` fromIntegral (gridSize (Proxy @(y:xs))))-      remainder = toFinite (fromFinite n `mod` gridSize (Proxy @(y:xs)))-  fromCoord _ (x :# ys) =-    toFinite $ firstPart + rest-      where-        firstPart = fromFinite x * gridSize (Proxy @(y:xs))-        rest = fromFinite (fromCoord (Proxy @(y:xs)) ys)-  nestLists _ v = nestLists (Proxy @(y:xs)) <$> chunkVector (Proxy @(GridSize (y:xs))) v-  unNestLists _ xs = concat (unNestLists (Proxy @(y:xs)) <$> xs)+  -- * Collapsing+  , toNestedLists -instance (Dimensions dims) => Distributive (Grid dims) where-  distribute = distributeRep+  -- * Indexing+  , Coord(..)+  , coord+  , unconsC+  , appendC+  , Rep.index -instance (Dimensions dims) => Representable (Grid dims) where-  type Rep (Grid dims) = Coord dims-  index (Grid v) ind = v V.! fromIntegral (fromCoord (Proxy @dims) ind)-  tabulate f = Grid $ V.generate (fromIntegral $ gridSize (Proxy @dims)) (f . toCoord (Proxy @dims) . fromIntegral)+  -- * Updating+  , (//) --- | Computes the level of nesting requried to represent a given grid--- dimensionality as a nested list------ > NestedLists [2, 3] Int == [[Int]]--- > NestedLists [2, 3, 4] Int == [[[Int]]]-type family NestedLists (dims :: [Nat]) a where-  NestedLists '[] a = a-  NestedLists (_:xs) a = [NestedLists xs a]+  -- * Lenses+  , cell --- | Build a grid by selecting an element for each element-generate :: forall dims a . Dimensions dims => (Int -> a) -> Grid dims a-generate f = Grid $ V.generate (gridSize (Proxy @dims)) f+  -- * Convolution+  , autoConvolute+  , convolute --- | Build a grid by selecting an element for each coordinate-generateCoord-  :: forall dims a . Dimensions dims => (Coord dims -> a) -> Grid dims a-generateCoord f = generate (f . toCoord (Proxy @dims) . fromIntegral)+  -- ** Window restriction+  , clampWindow+  , wrapWindow+  , safeWindow -chunkVector :: forall n a . KnownNat n => Proxy n -> V.Vector a -> [V.Vector a]-chunkVector _ v-  | V.null v-  = []-  | otherwise-  = let (before, after) = V.splitAt (fromIntegral $ natVal (Proxy @n)) v-    in  before : chunkVector (Proxy @n) after+  -- * Permutations+  , transpose+  , permute+  , permuteCoord --- | Turn a grid into a nested list structure. List nesting increases for each--- dimension------ > toNestedLists (G.generate id :: Grid [2, 3] Int)--- > [[0,1,2],[3,4,5]]-toNestedLists-  :: forall dims a . (Dimensions dims) => Grid dims a -> NestedLists dims a-toNestedLists (Grid v) = nestLists (Proxy @dims) v+  -- * Joining+  , joinGrid+  , splitGrid --- | Turn a nested list structure into a Grid if the list is well formed. --- Required list nesting increases for each dimension------ > fromNestedLists [[0,1,2],[3,4,5]] :: Maybe (Grid [2, 3] Int)--- > Just (Grid [[0,1,2],[3,4,5]])--- > fromNestedLists [[0],[1,2]] :: Maybe (Grid [2, 3] Int)--- > Nothing-fromNestedLists-  :: forall dims a-   . Dimensions dims-  => NestedLists dims a-  -> Maybe (Grid dims a)-fromNestedLists = fromList . unNestLists (Proxy @dims)+  -- * Assorted+  , gridSize --- | Convert a list into a Grid or fail if not provided the correct number of--- elements------ > G.fromList [0, 1, 2, 3, 4, 5] :: Maybe (Grid [2, 3] Int)--- > Just (Grid [[0,1,2],[3,4,5]])--- > G.fromList [0, 1, 2, 3] :: Maybe (Grid [2, 3] Int)--- > Nothing-fromList-  :: forall a dims-   . (KnownNat (GridSize dims), Dimensions dims)-  => [a]-  -> Maybe (Grid dims a)-fromList xs =-  let v = V.fromList xs-  in  if V.length v == gridSize (Proxy @dims) then Just $ Grid v else Nothing+  -- * Typeclasses & Type Families+  , Dimensions+  , NestedLists+  , Neighboring+  , ValidPermutation+  , Permuted+  )+where --- | Update elements of a grid-(//)-  :: forall dims a-   . (Dimensions dims)-  => Grid dims a-  -> [(Coord dims, a)]-  -> Grid dims a-(Grid v) // xs =-  Grid (v V.// fmap (first (fromFinite . fromCoord (Proxy @dims))) xs)+import           Data.Grid.Internal.Grid+import           Data.Grid.Internal.Nest+import           Data.Grid.Internal.Lens+import           Data.Grid.Internal.Transpose+import           Data.Grid.Internal.Coord+import           Data.Grid.Internal.Convolution+import           Data.Functor.Rep as Rep
+ src/Data/Grid/Examples/Conway.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedLists #-}+module Data.Grid.Examples.Conway where++import Data.Grid+import Data.Foldable+import Data.List++rule :: Grid '[3, 3] Bool -> Bool+rule g = (currentCellAlive && livingNeighbours == 2) || livingNeighbours == 3+ where+  currentCellAlive = g `index` Coord [1, 1] -- Get the center cell+  livingNeighbours =+    (if currentCellAlive then subtract 1 else id) . length . filter id $ toList+      g++step :: (Dimensions dims) => Grid dims Bool -> Grid dims Bool+step = autoConvolute wrapWindow rule++glider :: [Coord '[10, 10]]+glider = Coord <$> [[0, 1], [1, 2], [2, 0], [2, 1], [2, 2]]++start :: Grid '[10, 10] Bool+start = tabulate (`elem` glider)++simulate :: Int -> Grid '[10, 10] Bool+simulate = (iterate step start !!)++showBool :: Bool -> Char+showBool True  = '#'+showBool False = '.'++showGrid :: (Dimensions '[x, y]) => Grid '[x, y] Bool -> String+showGrid = intercalate "\n" . toNestedLists . fmap showBool
+ src/Data/Grid/Examples/Intro.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+module Data.Grid.Examples.Intro where++import Data.Grid+import Data.Maybe+import Data.Functor.Compose+import Data.Coerce+import Data.Foldable+import Data.Functor.Rep+import GHC.TypeNats hiding ()++simpleGrid :: Grid '[5, 5] Int+simpleGrid = generate id++coordGrid :: Grid '[5, 5] (Coord '[5, 5])+coordGrid = tabulate id+++avg :: Foldable f => f Int -> Int+avg f | null f    = 0+      | otherwise = sum f `div` length f++mx :: Foldable f => f Int -> Int+mx = maximum++small :: Grid '[3, 3] Int+small = generate id++small' :: Grid '[5, 5] Int+small' = generate id+++med :: Grid '[3, 3, 3] Int+med = generate id++big :: Grid '[5, 5, 5, 5] Int+big = generate id++gauss :: (Dimensions dims) => Grid dims Double -> Grid dims Double+gauss = autoConvolute safeWindow gauss'+ where+  gauss' :: Compose (Grid '[3, 3]) Maybe Double -> Double+  gauss' g = (sum g) / fromIntegral (length g)++clampGauss :: (Dimensions dims) => Grid dims Double -> Grid dims Double+clampGauss = autoConvolute clampWindow gauss'+ where+  gauss' :: Grid '[3, 3] Double -> Double+  gauss' g = sum g / fromIntegral (length g)+++seeNeighboring :: Grid '[3, 3] a -> Grid '[3, 3] (Grid '[3, 3] (Maybe a))+seeNeighboring = autoConvolute safeWindow go+ where+  go :: Compose (Grid '[3, 3]) Maybe a -> Grid '[3, 3] (Maybe a)+  go = getCompose . coerce++coords :: Grid '[3, 3] (Coord '[3, 3])+coords = tabulate id++doubleGrid :: Grid '[3, 3] Double+doubleGrid = fromIntegral <$> small++simpleGauss :: Grid '[3, 3] Double+simpleGauss = gauss doubleGrid++pacmanGauss :: (Dimensions dims) => Grid dims Double -> Grid dims Double+pacmanGauss = autoConvolute @'[3, 3] wrapWindow gauss'+  where gauss' g = sum g / fromIntegral (length g)
+ src/Data/Grid/Internal/Convolution.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+module Data.Grid.Internal.Convolution where++import           Data.Grid.Internal.Grid+import           Data.Grid.Internal.Coord+import           Data.Grid.Internal.Nest+import           Data.Functor.Rep+import           GHC.TypeNats+import           Data.Kind+import           Control.Applicative+import           Data.Functor.Compose+import           Data.Foldable+import           Data.Coerce++import           Control.Comonad+import           Control.Comonad.Representable.Store+import           Data.Maybe+import           Data.Proxy++criticalError :: a+criticalError = error+  "Something went wrong, please report this issue to the maintainer of grids"++-- | Perform a computation based on the context surrounding a cell+-- Good for doing things like Linear Image Filters (e.g. gaussian blur) or+-- simulating Cellular Automata (e.g. Conway's game of life)+--+-- This function accepts a function which indicates what to do with+-- 'out-of-bounds' indexes, 'clampWindow', 'wrapWindow' and 'safeWindow'+-- are examples.+--+-- It also acccepts a transformation function which operates over the+-- functor created by the first parameter and collapses it down to a new+-- value for the cell at that position.+--+-- This function is best used with Type Applications to denote the desired+-- window size; the Grid passed to the given function contains the current cell+-- (in the middle) and all the surrounding cells.+--+-- Here's an example of computing the average of all neighbouring cells,+-- repeating values at the edge of the grid when indexes are out of bounds+-- (using 'clampWindow')+--+-- > gaussian :: (Dimensions dims) => Grid dims Double -> Grid dims Double+-- > gaussian = autoConvolute clampWindow avg+-- >  where+-- >   avg :: Grid '[3, 3] Double -> Double+-- >   avg g = sum g / fromIntegral (length g)+autoConvolute+  :: forall window dims f a b+   . ( Dimensions dims+     , Dimensions window+     , Functor f+     , Neighboring window+     )+  => (Grid window (Coord dims) -> f (Coord dims)) -- ^ Restrict out of bounds coordinates in some way. Use 'clampWindow', 'wrapWindow' or 'safeWindow'+  -> (f a -> b) -- ^ Collapse the context down to a value+  -> Grid dims a -- ^ Starting grid+  -> Grid dims b+autoConvolute restrict = convolute (restrict . window @window @dims)++-- | This is a fully generic version of 'autoConvolute' which allows+-- the user to provide a function which builds a context from the current+-- coord, then provides a collapsing function over the same functor.+convolute+  :: forall dims f a b+   . (Functor f, Dimensions dims)+  => (Coord dims -> f (Coord dims))  -- ^ Build a neighbouring context within a functor from the current coord+  -> (f a -> b) -- ^ Collapse the context to a single value+  -> Grid dims a -- ^ Starting grid+  -> Grid dims b+convolute selectWindow f g =+  let s = store (index g) criticalError+      convoluted :: Store (Grid dims) b+      convoluted     = extend (f . experiment (fmap roundTrip . selectWindow)) s+      (tabulator, _) = runStore convoluted+  in  tabulate tabulator+ where+  roundTrip :: Coord dims -> Coord dims+  roundTrip = toEnum . fromEnum++-- | Given a coordinate generate a grid of size 'window' filled with+-- coordinates surrounding the given coord. Mostly used internally+window+  :: forall window dims+   . (Neighboring window, Dimensions window)+  => Coord dims+  -> Grid window (Coord dims)+window = fromWindow . neighboring . toWindow+ where+  toWindow :: Coord dims -> Coord window+  toWindow = coerceCoordDims+  fromWindow :: Grid window (Coord window) -> Grid window (Coord dims)+  fromWindow = fmap coerceCoordDims++-- data Orth a =+--   Orth+--     { up :: a+--     , right :: a+--     , down :: a+--     , left :: a+--     } deriving (Eq, Show, Functor, Traversable, Foldable)++-- orthNeighbours :: Coord dims  -> Compose Orth Maybe (Coord dims )+-- orthNeighbours c = Compose+--   (   toMaybe+--   <$> traverse+--         (+)+--         Orth {up = 0 :# (-1), right = 1 :# 0, down = 0 :# 1, left = -1 :# 0}+--         c+--   )+--  where+--   toMaybe c@(x :# y) | not (inBounds x) || not (inBounds y) = Nothing+--                      | otherwise                            = Just c++-- orthFromList [up', right', down', left'] =+--   Orth {up = up, right = right', down = down', left = left'}++class Neighboring dims where+  neighbors :: Grid dims (Coord dims)++instance {-# OVERLAPPING #-} (KnownNat n) => Neighboring '[n]  where+  neighbors = fromList' . fmap (Coord . pure . subtract (numVals `div` 2)) . take numVals $ [0 .. ]+    where+      numVals = gridSize @'[n]++instance (KnownNat n, Neighboring ns) => Neighboring (n:ns) where+  neighbors = joinGrid (addCoord <$> currentLevelNeighbors)+    where+      addCoord :: Coord '[n]  -> Grid ns (Coord (n : ns) )+      addCoord c = appendC c <$> nestedNeighbors+      nestedNeighbors :: Grid ns (Coord ns )+      nestedNeighbors = neighbors+      currentLevelNeighbors :: Grid '[n] (Coord '[n] )+      currentLevelNeighbors = neighbors++neighboring :: (Dimensions dims, Neighboring dims) => Coord dims -> Grid dims (Coord dims)+neighboring c = (c +) <$> neighbors++-- | Use with 'autoConvolute'; Clamp out-of-bounds coordinates to the nearest in-bounds coord.+clampWindow+  :: (Dimensions dims) => Grid window (Coord dims) -> Grid window (Coord dims)+clampWindow = fmap clampCoord++-- | Use with 'autoConvolute'; Wrap out-of-bounds coordinates pac-man style to the other side of the grid+wrapWindow+  :: (Dimensions dims) => Grid window (Coord dims) -> Grid window (Coord dims)+wrapWindow = fmap wrapCoord++-- | Use with 'autoConvolute'; Out of bounds coords become 'Nothing'+safeWindow+  :: (Dimensions dims) => Grid window (Coord dims) -> Compose (Grid window) Maybe (Coord dims)+safeWindow = Compose . fmap wrap+  where+    wrap c | coordInBounds c = Just c+           | otherwise  = Nothing
+ src/Data/Grid/Internal/Coord.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeFamilyDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ViewPatterns #-}++module Data.Grid.Internal.Coord where++import           GHC.Exts+import           GHC.TypeNats                      hiding ( Mod )+import           GHC.TypeLits hiding (natVal, Mod)+import           Data.Proxy+import           Data.Kind+import           Unsafe.Coerce+import           Data.Coerce+import           Data.List+import           Data.Singletons.Prelude++-- | The index type for 'Grid's.+newtype Coord (dims :: [Nat]) = Coord {unCoord :: [Int]}+  deriving (Eq)++-- | Safely construct a 'Coord' for a given grid size, checking that all+-- indexes are in range+--+-- > λ> coord @[3, 3] [1, 2]+-- > Just [1, 2]+-- >+-- > λ> coord @[3, 3] [3, 3]+-- > Nothing+-- >+-- > λ> coord @[3, 3] [1, 2, 3]+-- > Nothing+coord :: forall dims. SingI dims => [Int] -> Maybe (Coord dims)+coord ds = if inRange && correctLength then Just (Coord ds)+                      else Nothing+  where+    inRange = all (>=0) ds && all id (zipWith (<) ds (fromIntegral <$> demote @dims))+    correctLength = length ds == length (demote @dims)++instance IsList (Coord dims) where+  type Item (Coord dims) = Int+  fromList = coerce+  toList = coerce++instance Show (Coord dims)+  where+    show (Coord cs) = "[" ++ intercalate ", " (show <$> cs) ++ "]"++-- | Get the first index from a 'Coord'+unconsC :: Coord (n : ns) -> (Int, Coord ns)+unconsC (Coord (n : ns)) = (n, Coord ns)++-- | Append two 'Coord's+appendC :: Coord ns -> Coord ms -> Coord (ns ++ ms) +appendC (Coord ns) (Coord ms) = Coord (ns ++ ms)++pattern (:#) :: Int -> Coord ns -> Coord (n:ns) +pattern n :# ns <- (unconsC -> (n, ns)) where+  n :# (unCoord -> ns) = Coord (n:ns)++instance (Enum (Coord ns)) => Num (Coord ns ) where+  (Coord xs) + (Coord ys) = Coord (zipWith (+) xs ys)+  a - b = a + (negate b)+  (Coord xs) * (Coord ys) = Coord (zipWith (*) xs ys)+  abs (Coord xs) = Coord (abs <$> xs)+  signum (Coord xs) = Coord (signum <$> xs)+  fromInteger = toEnum . fromIntegral+  negate (Coord xs) = Coord (negate <$> xs)++highestIndex :: forall n. KnownNat n => Int+highestIndex = fromIntegral $ natVal (Proxy @n) - 1++clamp :: Int -> Int -> Int -> Int+clamp start end = max start . min end++clampCoord :: forall dims. SingI dims => Coord dims -> Coord dims+clampCoord (Coord ns) = Coord (zipWith (clamp 0 . fromIntegral) (demote @dims) ns)++wrapCoord :: forall dims. SingI dims => Coord dims -> Coord dims+wrapCoord (Coord ns) = Coord (zipWith mod  ns (fromIntegral <$> demote @dims)) ++instance Bounded (Coord '[] ) where+  minBound = Coord []+  maxBound = Coord []++instance (KnownNat n, Bounded (Coord ns )) => Bounded (Coord (n:ns) ) where+  minBound = 0 :# minBound+  maxBound = highestIndex @n :# maxBound++instance  (KnownNat n) => Enum (Coord '[n]) where+  toEnum i = Coord [i]+  fromEnum (Coord [i]) = clamp 0 (highestIndex @n) i++instance  (KnownNat x, KnownNat y, SingI rest, Bounded (Coord rest ), Enum (Coord (y:rest) )) => Enum (Coord (x:y:rest) ) where+  toEnum i | i < 0 = negate $ toEnum (abs i)+  toEnum i | i > fromEnum (maxBound @(Coord (x:y:rest) )) = error "Index out of bounds"+  toEnum i = (i `div` (gridSize @(y:rest))) :# toEnum (i `mod` gridSize @(y:rest))+  fromEnum (x :# ys) = (clamp 0 (highestIndex @x) x * gridSize @(y:rest)) + fromEnum ys++-- | Get the total size of a 'Grid' of the given dimensions+--+-- > gridSize @'[2, 2] == 4+gridSize :: forall (dims :: [Nat]) . SingI dims => Int+gridSize = product . fmap fromIntegral $ demote @dims++coerceCoordDims :: Coord ns -> Coord ms+coerceCoordDims = unsafeCoerce++coordInBounds :: forall ns. (SingI ns) => Coord ns -> Bool+coordInBounds (Coord cs) = all inRange $ zip cs maxIndexes+  where+    maxIndexes = fromIntegral <$> demote @ns+    inRange (val,upperBound) = val >= 0 && val < upperBound
+ src/Data/Grid/Internal/Errors.hs view
@@ -0,0 +1,11 @@+{-# LANGUAGE UndecidableInstances #-}+module Data.Grid.Internal.Errors where++import Data.Kind+import GHC.TypeLits++type family (b :: Bool) ?! (e :: ErrorMessage) :: Constraint where+  True ?! _ = ()+  False ?! e = TypeError e++infixr 1 ?!
+ src/Data/Grid/Internal/Grid.hs view
@@ -0,0 +1,124 @@+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++module Data.Grid.Internal.Grid+  ( Grid(..)+  , Dimensions(..)+  , Coord+  , NestedLists+  , generate+  , toNestedLists+  , fromNestedLists+  , fromNestedLists'+  , fromList+  , fromList'+  , (//)+  )+where++import           Data.Grid.Internal.NestedLists+import           Data.Grid.Internal.Coord+import           Data.Grid.Internal.Pretty+import           Data.Distributive+import           Data.Functor.Rep+import qualified Data.Vector                   as V+import           Data.Proxy+import           Data.Kind+import           GHC.TypeNats                  as N+                                                   hiding ( Mod )+import           Control.Applicative+import           Data.List+import           Data.Bifunctor+import           Data.Maybe+import           Data.Singletons.Prelude+import           Control.DeepSeq++-- | An grid of arbitrary dimensions.+--+-- e.g. a @Grid [2, 3] Int@ might look like:+--+-- > generate id :: Grid [2, 3] Int+-- > fromNestedLists [[0,1,2],+-- >                  [3,4,5]]+newtype Grid (dims :: [Nat]) a =+  Grid  {toVector :: V.Vector a}+  deriving (Eq, Functor, Foldable, Traversable, NFData)++instance (PrettyList (NestedLists dims a), Dimensions dims, Show (NestedLists dims a)) => Show (Grid dims a) where+  show g = "fromNestedLists \n" ++ (unlines . fmap ("  " ++ ) . lines $ prettyList (toNestedLists g))++instance (Dimensions dims, Semigroup a) => Semigroup (Grid dims a) where+  (<>) = liftA2 (<>)++instance (Dimensions dims, Monoid a) => Monoid (Grid dims a) where+  mempty = pure mempty++instance (Dimensions dims) => Applicative (Grid dims) where+  pure a = tabulate (const a)+  liftA2 f (Grid v) (Grid u) = Grid $ V.zipWith f v u++instance (Dimensions dims) => Distributive (Grid dims) where+  distribute = distributeRep++instance (Dimensions dims) => Representable (Grid dims) where+  type Rep (Grid dims) = Coord dims+  index (Grid v) c = v V.! fromEnum c+  tabulate f = Grid $ V.generate (fromIntegral $ gridSize @dims) (f . toEnum  . fromIntegral)++-- | Build a grid by selecting an element for each element+generate :: forall dims a . (SingI dims) => (Int -> a) -> Grid dims a+generate f = Grid $ V.generate (gridSize @dims) f++-- | Turn a grid into a nested list structure. List nesting increases for each+-- dimension+--+-- > toNestedLists (G.generate id :: Grid [2, 3] Int)+-- > [[0,1,2],[3,4,5]]+toNestedLists+  :: forall dims a . (Dimensions dims) => Grid dims a -> NestedLists dims a+toNestedLists (Grid v) = nestLists (Proxy @dims) v++-- | Turn a nested list structure into a Grid if the list is well formed. +-- Required list nesting increases for each dimension+--+-- > fromNestedLists [[0,1,2],[3,4,5]] :: Maybe (Grid [2, 3] Int)+-- > Just (Grid [[0,1,2],[3,4,5]])+-- > fromNestedLists [[0],[1,2]] :: Maybe (Grid [2, 3] Int)+-- > Nothing+fromNestedLists+  :: forall dims a+   . Dimensions dims+  => NestedLists dims a+  -> Maybe (Grid dims a)+fromNestedLists = fromList . unNestLists (Proxy @dims)++-- | Partial variant of 'fromNestedLists' which errors on malformed input+fromNestedLists'+  :: forall dims a . Dimensions dims => NestedLists dims a -> Grid dims a+fromNestedLists' = fromJust . fromNestedLists++-- | Convert a list into a Grid or fail if not provided the correct number of+-- elements+--+-- > G.fromList [0, 1, 2, 3, 4, 5] :: Maybe (Grid [2, 3] Int)+-- > Just (Grid [[0,1,2],[3,4,5]])+-- > G.fromList [0, 1, 2, 3] :: Maybe (Grid [2, 3] Int)+-- > Nothing+fromList :: forall dims a . (SingI dims) => [a] -> Maybe (Grid dims a)+fromList xs =+  let v = V.fromList xs+  in  if V.length v == gridSize @dims then Just $ Grid v else Nothing++-- | Partial variant of 'fromList' which errors on malformed input+fromList' :: forall dims a . (SingI dims) => [a] -> Grid dims a+fromList' = fromJust . fromList++-- | Update elements of a grid+(//)+  :: forall dims a+   . (Enum (Coord dims ))+  => Grid dims a+  -> [(Coord dims , a)]+  -> Grid dims a+(Grid v) // xs = Grid (v V.// fmap (first fromEnum) xs)
+ src/Data/Grid/Internal/Identity.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE PolyKinds #-}+module Data.Grid.Internal.Identity where++import Data.Grid.Internal.Grid+import Data.Vector as V+import Data.Proxy+import GHC.TypeNats++-- idMatrix+--   :: forall (n :: Nat) (ns :: [Nat]) ind x+--    . (Num x, Dimensions (n : ns), Dimensions ns)+--   => Grid (n : ns) x+-- idMatrix = Grid ns+--  where+--   ns = V.generate (inhabitants @(n : ns)) thing+--   thing n = if n `mod` (inhabitants @ns + 1) == 0 then 1 else 0
+ src/Data/Grid/Internal/Lens.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+module Data.Grid.Internal.Lens where++import Data.Grid.Internal.Grid+import Data.Functor.Rep as R+import Data.Vector as V+import Data.Proxy++type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t+type Lens' s a  = Lens s s a a++lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b+lens sa sbt afb s = sbt s <$> afb (sa s)++-- | Focus an element of a 'Grid' given its 'Coord'+cell+  :: forall ind dims a+   . (Dimensions dims)+  => Coord dims+  -> Lens' (Grid dims a) a+cell c = lens get set+ where+  get          = flip R.index c+  vectorOffset = fromEnum c+  set (Grid v) new = Grid (v V.// [(vectorOffset, new)])
+ src/Data/Grid/Internal/Nest.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+module Data.Grid.Internal.Nest where++import Data.Grid.Internal.Grid+import Data.Grid.Internal.Coord+import Data.Singletons.Prelude+import Data.Maybe+++-- | The inverse of 'splitGrid', +-- joinGrid will nest a grid from:+-- > Grid outer (Grid inner a) -> Grid (outer ++ inner) a+--+-- For example, you can nest a simple 3x3 from smaller [3] grids as follows:+--+-- > joinGrid (myGrid :: Grid [3] (Grid [3] a)) :: Grid '[3, 3] a+joinGrid :: Grid dims (Grid ns a) -> Grid (dims ++ ns) a+joinGrid (Grid v) = Grid (v >>= toVector)++-- | The inverse of 'joinGrid', +-- splitGrid @outerDims @innerDims will un-nest a grid from:+-- > Grid (outer ++ inner) a -> Grid outer (Grid inner a)+--+-- For example, you can unnest a simple 3x3 as follows:+--+-- > splitGrid @'[3] @'[3] myGrid :: Grid '[3] (Grid [3] a)+splitGrid+  :: forall outer inner a from+   . ( from ~ (outer ++ inner)+     , Dimensions from+     , Dimensions inner+     , Dimensions outer+     , NestedLists from a ~ NestedLists outer (NestedLists inner a)+     )+  => Grid from a+  -> Grid outer (Grid inner a)+splitGrid = fmap fromNestedLists' . fromNestedLists' . toNestedLists
+ src/Data/Grid/Internal/NestedLists.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses #-}+module Data.Grid.Internal.NestedLists where++import           Data.Kind+import           GHC.TypeNats                  as N+import           Data.Singletons.Prelude+import qualified Data.Vector                   as V+import           Data.List+import           Data.Grid.Internal.Coord++type family AllC (c :: x -> Constraint) (ts :: [x]) :: Constraint where+  AllC c '[] = ()+  AllC c (x:xs) = (c x, AllC c xs)++-- | Computes the level of nesting requried to represent a given grid+-- dimensionality as a nested list+--+-- > NestedLists [2, 3] Int == [[Int]]+-- > NestedLists [2, 3, 4] Int == [[[Int]]]+type family NestedLists (dims :: [Nat]) a where+  NestedLists '[] a = a+  NestedLists (_:xs) a = [NestedLists xs a]++chunkVector :: forall a . Int -> V.Vector a -> [V.Vector a]+chunkVector n v+  | V.null v+  = []+  | otherwise+  = let (before, after) = V.splitAt n v in before : chunkVector n after+++-- | Represents valid dimensionalities. All non empty lists of Nats have+-- an instance+class (AllC KnownNat dims, SingI dims, Enum (Coord dims), Bounded (Coord dims)) => Dimensions  (dims :: [Nat]) where+  nestLists :: Proxy dims -> V.Vector a -> NestedLists dims a+  unNestLists :: Proxy dims -> NestedLists dims a -> [a]++instance (KnownNat x) => Dimensions '[x] where+  nestLists _ = V.toList+  unNestLists _ xs = xs++instance (KnownNat x, Bounded (Coord xs), SingI xs, Dimensions (y:xs)) => Dimensions (x:y:xs) where+  nestLists _ v = nestLists (Proxy @(y:xs)) <$> chunkVector (gridSize @(y:xs)) v+  unNestLists _ xs = concat (unNestLists (Proxy @(y:xs)) <$> xs)
+ src/Data/Grid/Internal/Pretty.hs view
@@ -0,0 +1,18 @@+module Data.Grid.Internal.Pretty where++import Data.List++class PrettyList l where+  prettyList :: l -> String++instance {-# OVERLAPPABLE #-} (Show a) => PrettyList [a] where+  prettyList = show++instance {-# OVERLAPPABLE #-} (Show a) => PrettyList [[a]] where+  prettyList ls = "[" ++ intercalate "\n," (prettyList <$> ls) ++ "]"++instance (Show a) => PrettyList [[[ a ]]] where+  prettyList ls = "[" ++ intercalate "\n\n," (unlines . overRest (" " ++ ) . lines . prettyList <$> ls) ++ "]"+    where+      overRest f (l:ls) = l : fmap f ls+      overRest f ls = ls
+ src/Data/Grid/Internal/Transpose.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE PolyKinds #-}+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+module Data.Grid.Internal.Transpose where++import           Data.Grid.Internal.Grid+import           Data.Grid.Internal.Errors+import           Data.Grid.Internal.Coord+import           GHC.TypeNats+import           GHC.TypeLits                  as TL+import           Data.Singletons.Prelude+import           Data.Singletons.Prelude.List  as L+import           Data.Singletons.Prelude.Maybe+import           Data.Functor.Rep+import           Data.Vector                   as V+import           Data.Kind+import           Data.Maybe+import           Data.List++type family Permuted (key :: [Nat]) (from :: [Nat]) :: [Nat] where+  Permuted '[] _ = '[]+  Permuted (x:xs) from = (from !! x) : Permuted xs from++type ValidPermutation key from =+  (Sort key == EnumFromTo 0 (Length from TL.- 1)) ?!+    (Text "Malformed permutation hint: " :<>: ShowType key+                :$$: Text "When permuting matrix of size: " :<>: ShowType from+                :$$: Text "Key must be a permutation of "  :<>: ShowType (EnumFromTo 0 (Length from TL.- 1))+                :$$: Text "e.g. the identity permutation for 2x2 is @[0, 1]"+                :$$: Text "e.g. matrix transpose for 2x2 is @[1, 0]"+  )++-- | Permute dimensions of a 'Grid'. This is similar to MatLab's permute+-- function+--+-- 'permute' requires a type application containing a permutation pattern;+-- The pattern is a re-ordering of the list @[0..n]@ which represents the new+-- dimension order. For example the permutation pattern @[1, 2, 0]@ when+-- applied to the dimensions @[4, 5, 6]@ results in the dimensions @[5, 6, 4]@.+--+-- For 2 dimensional matrixes, a permutation using @[1, 0]@ is simply a +-- matrix 'transpose'+--+-- > λ> small+-- > fromNestedLists+-- >   [[0,1,2]+-- >   ,[3,4,5]+-- >   ,[6,7,8]]+-- >+-- > λ> permute @[1, 0] small+-- > fromNestedLists+-- >   [[0,3,6]+-- >   ,[1,4,7]+-- >   ,[2,5,8]]+permute+  :: forall (key :: [Nat]) from a invertedKey+   . ( SingI invertedKey+     , invertedKey ~ InvertKey (EnumFromTo 0 (Length from TL.- 1)) key+     , ValidPermutation key from+     , Dimensions from +     , Dimensions (Permuted key from) +     )+  => Grid from a+  -> Grid (Permuted key from) a+permute (Grid v) = result+ where+  len = V.length v+  result :: Grid (Permuted key from) a+  result = tabulate+    ((v V.!) . fromEnum  . permuteCoord @invertedKey @from)++-- | Permute the dimensions of a coordinate according to a permutation pattern.+-- see 'permute' regarding permutation patterns+permuteCoord+  :: forall (key :: [Nat]) to from +   . (SingI key)+  => Coord from +  -> Coord to +permuteCoord (Coord cs) = Coord newCoord+ where+  key :: [Int]+  key = fromIntegral <$> demote @key+  newCoord :: [Int]+  newCoord = (cs !!) <$> key++-- | Transpose a 2 dimensional matrix. Equivalent to:+--+-- > permute @[1, 0]+transpose :: (KnownNat x, KnownNat y) => Grid '[x, y] a -> Grid '[y, x] a+transpose = permute @'[1, 0]++-- | Get the inverse of a permutation pattern, used internally+type family InvertKey ref key :: [Nat] where+  InvertKey '[] xs = '[]+  InvertKey (n:ns) xs = FromJust (ElemIndex n xs) : InvertKey ns xs
− src/Data/Grid/Lens.hs
@@ -1,28 +0,0 @@-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleContexts #-}-module Data.Grid.Lens (cell) where--import Data.Grid-import Data.Functor.Rep as R-import Data.Vector as V-import Data.Proxy--type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t-type Lens' s a  = Lens s s a a--lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b-lens sa sbt afb s = sbt s <$> afb (sa s)---- | Focus an element of a grid-cell-  :: forall dims a-   . (Dimensions dims, Eq (Coord dims))-  => Coord dims-  -> Lens' (Grid dims a) a-cell c = lens get set- where-  get          = flip R.index c-  vectorOffset = fromIntegral (fromCoord (Proxy @dims) c)-  set (Grid v) new = Grid (v V.// [(vectorOffset, new)])
+ test/Spec.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE TypeApplications #-}+module Spec where++import Test.Hspec+import qualified Data.Vector as V+import Data.Grid as G+import Control.Applicative+import Data.Maybe+import Data.Functor.Compose+++smallGrid :: Grid '[2, 2] Int+smallGrid = G.generate id++medGrid :: Grid '[3, 3] Int+medGrid = G.generate id++dim3Grid :: Grid '[2, 3, 4] Int+dim3Grid = G.generate id+++++main :: IO ()+main = hspec $ do+  describe "creation" $ do+    it "pure should make a grid of all one element" $ do+      let Grid v = pure 1 :: Grid '[2, 2] Int+      v `shouldBe` V.replicate 4 1++    it "generate should put things in the right places" $ do+      let g        = G.generate id :: Grid '[2, 2] Int+          expected = fromNestedLists [[0, 1], [2, 3]]+      Just g `shouldBe` expected++    it "tabulate should put coords in the right places" $ do+      let g        = tabulate id :: Grid '[2, 2] (Coord '[2, 2])+          expected = fromNestedLists+            [[Coord [0, 0], Coord [0, 1]], [Coord [1, 0], Coord [1, 1]]]+      Just g `shouldBe` expected++  describe "indexing" $ do+    it "index should retrieve correct elem" $ do+      (smallGrid `index` Coord [0, 0]) `shouldBe` 0+      (smallGrid `index` Coord [0, 1]) `shouldBe` 1+      (smallGrid `index` Coord [1, 0]) `shouldBe` 2+      (smallGrid `index` Coord [1, 1]) `shouldBe` 3++  describe "applicative" $ do+    it "should apply piecewise" $ do+      let expected = fromJust . fromNestedLists $ [[0, 2], [4, 6]]+      liftA2 (+) smallGrid smallGrid `shouldBe` expected++  describe "nested lists" $ do+    it "toNestedLists" $ toNestedLists smallGrid `shouldBe` [[0, 1], [2, 3]]+    it "fromNestedLists"+      $          fromNestedLists [[0, 1], [2, 3]]+      `shouldBe` Just smallGrid+    it "fromList" $ do+      G.fromList [0, 1, 2, 3] `shouldBe` Just smallGrid++  describe "updates" $ do+    it "(//)"+      $          (    smallGrid+                 G.// [(Coord [1, 1] :: Coord '[2, 2], 42), (Coord [0, 1], 100)]+                 )+      `shouldBe` fromNestedLists' [[0, 100], [2, 42]]++  describe "permutations" $ do+    it "transpose" $ do+      transpose smallGrid `shouldBe` fromNestedLists' [[0, 2], [1, 3]]+      transpose medGrid+        `shouldBe` fromNestedLists' [[0, 3, 6], [1, 4, 7], [2, 5, 8]]+    it "permute" $ do+      permute @'[1, 2, 0] dim3Grid `shouldBe` fromNestedLists'+        [ [[0, 12], [1, 13], [2, 14], [3, 15]]+        , [[4, 16], [5, 17], [6, 18], [7, 19]]+        , [[8, 20], [9, 21], [10, 22], [11, 23]]+        ]++  describe "convolutions" $ do+    it "autoConvolute with Clamp clamps out of bounds" $ do+      autoConvolute @'[3, 3] clampWindow toNestedLists smallGrid+        `shouldBe` fromNestedLists'+                     [ [ [[0, 0, 1], [0, 0, 1], [2, 2, 3]]+                       , [[0, 1, 1], [0, 1, 1], [2, 3, 3]]+                       ]+                     , [ [[0, 0, 1], [2, 2, 3], [2, 2, 3]]+                       , [[0, 1, 1], [2, 3, 3], [2, 3, 3]]+                       ]+                     ]++    it "autoConvolute with Mod wraps when out of bounds" $ do+      autoConvolute @'[3, 3] wrapWindow toNestedLists smallGrid+        `shouldBe` fromNestedLists'+                     [ [ [[3, 2, 3], [1, 0, 1], [3, 2, 3]]+                       , [[2, 3, 2], [0, 1, 0], [2, 3, 2]]+                       ]+                     , [ [[1, 0, 1], [3, 2, 3], [1, 0, 1]]+                       , [[0, 1, 0], [2, 3, 2], [0, 1, 0]]+                       ]+                     ]++    it "safeAutoConvolute gets 'Nothing' for out of bounds" $ do+      safeAutoConvolute @'[3, 3] toNestedLists smallGrid+        `shouldBe` fromNestedLists'+                     [ [ [ [Nothing, Nothing, Nothing]+                         , [Nothing, Just 0, Just 1]+                         , [Nothing, Just 2, Just 3]+                         ]+                       , [ [Nothing, Nothing, Nothing]+                         , [Just 0, Just 1, Nothing]+                         , [Just 2, Just 3, Nothing]+                         ]+                       ]+                     , [ [ [Nothing, Just 0, Just 1]+                         , [Nothing, Just 2, Just 3]+                         , [Nothing, Nothing, Nothing]+                         ]+                       , [ [Just 0, Just 1, Nothing]+                         , [Just 2, Just 3, Nothing]+                         , [Nothing, Nothing, Nothing]+                         ]+                       ]+                     ]