comfort-array-shape-0.0: src/Data/Array/Comfort/Shape/Extra.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GADTs #-}
module Data.Array.Comfort.Shape.Extra
{- DEPRECATED "use Data.Array.Comfort.Shape.Simplex instead" -}
(
Simplex(..),
) where
import qualified Data.Array.Comfort.Shape as Shape
import qualified Type.Data.Num.Unary as Unary
import Type.Data.Num (integralFromProxy)
import Type.Base.Proxy (Proxy(Proxy))
import qualified Data.Traversable as Trav
import qualified Data.Foldable as Fold
import qualified Data.FixedLength as FL
import qualified Data.NonEmpty as NonEmpty
import qualified Data.List.HT as ListHT
import Control.Applicative ((<$>))
{- $setup
>>> import qualified Data.Array.Comfort.Shape.Extra as ShapeExtra
>>> import qualified Data.Array.Comfort.Shape as Shape
>>>
>>> import qualified Type.Data.Num.Unary.Literal as TypeNum
>>> import qualified Type.Data.Num.Unary as Unary
-}
{- |
Simplex is a generalization of 'Shape.Triangular' to more than two dimensions.
Indices are tuples of fixed size
with elements ordered in ascending, strictly ascending,
descending or strictly descending order.
\"Order\" refers to the index order in 'indices'.
In order to avoid confusion we suggest that the order of 'indices'
is consistent with '<='.
Obviously, 'offset' implements ranking
and 'indexFromOffset' implements unranking
of combinations (in the combinatorial sense)
with or without repetitions.
>>> Shape.indices $ ShapeExtra.Simplex (Unary.unary TypeNum.u3) $ Shape.ZeroBased (4::Int)
[0!:1!:2!:end,0!:1!:3!:end,0!:2!:3!:end,1!:2!:3!:end]
-}
data Simplex d size =
Simplex {
simplexDimension :: UnaryProxy d,
simplexSize :: size
} deriving (Show)
type UnaryProxy d = Proxy (Unary.Un d)
-- cf. package combinatorial
binomials :: Integral a => a -> [a]
binomials n =
scanl (\acc (num,den) -> div (acc*num) den) 1 (zip [n, pred n ..] [1..n])
simplexLayoutSize :: Integral i => Int -> i -> i
simplexLayoutSize d n =
case drop d $ binomials n of
[] -> 0
m:_ -> m
instance (Unary.Natural d, Shape.C size) => Shape.C (Simplex d size) where
size (Simplex d sz) =
simplexLayoutSize (integralFromProxy d) (Shape.size sz)
headSingletonFromProxy ::
(Unary.Natural d) => UnaryProxy d -> Unary.HeadSingleton d
headSingletonFromProxy Proxy = Unary.headSingleton
predHeadSingleton :: Unary.HeadSingleton (Unary.Succ d) -> UnaryProxy d
predHeadSingleton Unary.Succ = Proxy
simplexIndices :: (Unary.Natural d) => UnaryProxy d -> [a] -> [FL.T d a]
simplexIndices d =
case headSingletonFromProxy d of
Unary.Zero -> const [FL.end]
m@Unary.Succ -> \as -> do
(a,ts) <- zip as $ NonEmpty.tail $ NonEmpty.tails as
(a FL.!:) <$> simplexIndices (predHeadSingleton m) ts
instance
(Unary.Natural d, Shape.Indexed size) =>
Shape.Indexed (Simplex d size) where
type Index (Simplex d size) = FL.T d (Shape.Index size)
indices (Simplex d sz) = simplexIndices d $ Shape.indices sz
inBounds (Simplex _d sz) ix =
let ixs = Fold.toList ix
getOffset = Shape.offset sz
in all (Shape.inBounds sz) ixs &&
and (ListHT.mapAdjacent (<) $ map getOffset ixs)
unifiedSizeOffset (Simplex d sz) =
let (n, getOffset) = Shape.unifiedSizeOffset sz
dInt = integralFromProxy d
in (simplexLayoutSize dInt n,
-- cf. Combinatorics.chooseRank
\ixs -> do
ks <- Trav.traverse getOffset $ Fold.toList ixs
return $
simplexLayoutSize dInt n - 1
-
sum
(zipWith simplexLayoutSize
(iterate pred dInt) (map (n-1-) ks)))
instance
(Unary.Natural d, Shape.InvIndexed size) =>
Shape.InvIndexed (Simplex d size) where
unifiedIndexFromOffset (Simplex d sh) k =
let n = Shape.size sh in
let dInt = integralFromProxy d in
Trav.sequenceA $ snd $
Trav.mapAccumL
(\(a,k0) m ->
case dropWhile ((<0) . snd) $
map (\bi -> (bi, k0 - simplexLayoutSize m (n-bi-1))) $
takeWhile (<n) $ iterate (1+) a of
[] -> error "unifiedIndexFromOffset: offset out of range"
(b,k1):_ -> ((b+1, k1), Shape.unifiedIndexFromOffset sh b))
(0, simplexLayoutSize dInt n - 1 - k)
(NonEmpty.init $ NonEmpty.scanl (-) dInt $ FL.repeat 1)