{- Copyright (c) 2008, Scott E. Dillard. All rights reserved. -}

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Packed vectors : use these whenever possible. The regular vector type is
-- just a gussied up linked list, but when vector functions are applied to
-- these types, bracketed by @'pack'@ and @'unpack'@, then things unfold into
-- perfectly optimized code.

module Data.Vec.Packed where

import Data.Vec.Base as V

-- * Packed Vector Types
data Vec2I = Vec2I {-#UNPACK#-} !Int 
                   {-#UNPACK#-} !Int 

data Vec3I = Vec3I {-#UNPACK#-} !Int 
                   {-#UNPACK#-} !Int 
                   {-#UNPACK#-} !Int

data Vec4I = Vec4I {-#UNPACK#-} !Int 
                   {-#UNPACK#-} !Int 
                   {-#UNPACK#-} !Int
                   {-#UNPACK#-} !Int

data Vec2F = Vec2F {-#UNPACK#-} !Float 
                   {-#UNPACK#-} !Float 

data Vec3F = Vec3F {-#UNPACK#-} !Float 
                   {-#UNPACK#-} !Float 
                   {-#UNPACK#-} !Float

data Vec4F = Vec4F {-#UNPACK#-} !Float 
                   {-#UNPACK#-} !Float 
                   {-#UNPACK#-} !Float
                   {-#UNPACK#-} !Float

data Vec2D = Vec2D {-#UNPACK#-} !Double 
                   {-#UNPACK#-} !Double 

data Vec3D = Vec3D {-#UNPACK#-} !Double 
                   {-#UNPACK#-} !Double 
                   {-#UNPACK#-} !Double

data Vec4D = Vec4D {-#UNPACK#-} !Double 
                   {-#UNPACK#-} !Double 
                   {-#UNPACK#-} !Double
                   {-#UNPACK#-} !Double

-- * Packed Matrix Types. 
type Mat22I = Vec2 Vec2I 
type Mat23I = Vec2 Vec3I 
type Mat33I = Vec3 Vec3I 
type Mat34I = Vec3 Vec4I 
type Mat44I = Vec4 Vec3I 

type Mat22F = Vec2 Vec2F 
type Mat23F = Vec2 Vec3F 
type Mat33F = Vec3 Vec3F 
type Mat34F = Vec3 Vec4F 
type Mat44F = Vec4 Vec3F 

type Mat22D = Vec2 Vec2D 
type Mat23D = Vec2 Vec3D 
type Mat33D = Vec3 Vec3D 
type Mat34D = Vec3 Vec4D 
type Mat44D = Vec4 Vec4D 


-- | pack a matrix
packMat ::  (Map v pv m pm, PackedVec pv v) => m -> pm
packMat = V.map pack 
{-# INLINE packMat #-}

-- | unpack a matrix
unpackMat ::  (Map pv v pm m, PackedVec pv v) => pm -> m
unpackMat = V.map unpack
{-# INLINE unpackMat #-}

-- | PackedVec class : relates a packed vector type to its unpacked type For
-- now, the fundep is not bijective -- It may be advantageous to have multiple
-- packed representations for a canonical vector type. This may change. In the
-- meantime, you may have to annotate return types.
class PackedVec pv v | pv -> v  where
  pack   :: v -> pv
  unpack :: pv -> v

instance PackedVec Vec2I (Vec2 Int) where
  pack (x:.y:.()) = Vec2I x y 
  unpack (Vec2I x y) = x:.y:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}

instance PackedVec Vec3I (Vec3 Int) where
  pack (x:.y:.z:.()) = Vec3I x y z
  unpack (Vec3I x y z) = x:.y:.z:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}

instance PackedVec Vec4I (Vec4 Int) where
  pack (x:.y:.z:.w:.()) = Vec4I x y z w
  unpack (Vec4I x y z w) = x:.y:.z:.w:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}


instance PackedVec Vec2F (Vec2 Float) where
  pack (x:.y:.()) = Vec2F x y 
  unpack (Vec2F x y) = x:.y:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}

instance PackedVec Vec3F (Vec3 Float) where
  pack (x:.y:.z:.()) = Vec3F x y z
  unpack (Vec3F x y z) = x:.y:.z:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}

instance PackedVec Vec4F (Vec4 Float) where
  pack (x:.y:.z:.w:.()) = Vec4F x y z w
  unpack (Vec4F x y z w) = x:.y:.z:.w:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}


instance PackedVec Vec2D (Vec2 Double) where
  pack (x:.y:.()) = Vec2D x y 
  unpack (Vec2D x y) = x:.y:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}

instance PackedVec Vec3D (Vec3 Double) where
  pack (x:.y:.z:.()) = Vec3D x y z
  unpack (Vec3D x y z) = x:.y:.z:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}

instance PackedVec Vec4D (Vec4 Double) where
  pack (x:.y:.z:.w:.()) = Vec4D x y z w
  unpack (Vec4D x y z w) = x:.y:.z:.w:.()
  {-# INLINE pack #-}
  {-# INLINE unpack #-}