{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
module LLVM.Extra.Multi.Vector (
   T(Cons), consPrim, deconsPrim,
   C(..),
   Value(Value),
   map,
   zip, zip3, unzip, unzip3,
   replicate,
   iterate,

   lift1,

   modify,
   assemble,
   dissect,
   dissectList,

   reverse,
   rotateUp,
   rotateDown,
   shiftUp,
   shiftDown,
   shiftUpMultiZero,
   shiftDownMultiZero,

   undefPrimitive,
   shuffleMatchPrimitive,
   extractPrimitive,
   insertPrimitive,

   shuffleMatchTraversable,
   insertTraversable,
   extractTraversable,

   Additive(..),
   PseudoRing(..),
   Field(..),
   PseudoModule(..),
   Real(..),
   Fraction(..),
   Algebraic(..),
   Transcendental(..),
   FloatingComparison(..),
   Comparison(..),
   Logic(..),
   BitShift(..),
   ) where

import qualified LLVM.Extra.Multi.Value as MultiValue
import qualified LLVM.Extra.ScalarOrVector as SoV
import qualified LLVM.Extra.Arithmetic as A
import qualified LLVM.Extra.Class as Class
import LLVM.Extra.Multi.Value (Repr, )

import qualified LLVM.Util.Loop as Loop
import qualified LLVM.Core as LLVM
import LLVM.Util.Loop (Phi, )
import LLVM.Core
   (valueOf, value,
    IsPrimitive,
    CodeGenFunction, )

import qualified Type.Data.Num.Decimal as TypeNum

import qualified Data.Traversable as Trav
import qualified Data.NonEmpty as NonEmpty
import qualified Data.List as List
import Data.Traversable (mapM, sequence, )
import Data.NonEmpty ((!:), )
import Data.Function (flip, (.), ($), )
import Data.Tuple (snd, )
import Data.Maybe (maybe, )
import Data.List (take, (++), )
import Data.Word (Word8, Word16, Word32, Word64, )
import Data.Int (Int8, Int16, Int32, Int64, )
import Data.Bool (Bool, )

import qualified Control.Applicative as App
import qualified Control.Monad.HT as Monad
import Control.Monad.HT ((<=<), )
import Control.Monad (Monad, foldM, fmap, (>>), (=<<), )
import Control.Applicative (liftA2, )

import Prelude (Float, Double, Integer, Int, Rational, fromIntegral, (-), error, )


newtype T n a = Cons (Repr (Value n) a)

newtype Value n a = Value (PrimValue n a)


consPrim ::
   (Repr (Value n) a ~ Value n a) =>
   LLVM.Value (LLVM.Vector n a) -> T n a
consPrim = Cons . Value

deconsPrim ::
   (Repr (Value n) a ~ Value n a) =>
   T n a -> LLVM.Value (LLVM.Vector n a)
deconsPrim (Cons (Value a)) = a


instance (TypeNum.Positive n, C a) => Class.Undefined (T n a) where
   undefTuple = undef

instance (TypeNum.Positive n, C a) => Class.Zero (T n a) where
   zeroTuple = zero

instance (TypeNum.Positive n, C a) => Phi (T n a) where
   phis = phis
   addPhis = addPhis


size :: TypeNum.Positive n => T n a -> Int
size =
   let sz :: TypeNum.Positive n => TypeNum.Singleton n -> T n a -> Int
       sz n _ = TypeNum.integralFromSingleton n
   in  sz TypeNum.singleton


zip :: T n a -> T n b -> T n (a,b)
zip (Cons a) (Cons b) = Cons (a,b)

zip3 :: T n a -> T n b -> T n c -> T n (a,b,c)
zip3 (Cons a) (Cons b) (Cons c) = Cons (a,b,c)

unzip :: T n (a,b) -> (T n a, T n b)
unzip (Cons (a,b)) = (Cons a, Cons b)

unzip3 :: T n (a,b,c) -> (T n a, T n b, T n c)
unzip3 (Cons (a,b,c)) = (Cons a, Cons b, Cons c)


class (MultiValue.C a) => C a where
   undef :: (TypeNum.Positive n) => T n a
   zero :: (TypeNum.Positive n) => T n a
   phis ::
      (TypeNum.Positive n) =>
      LLVM.BasicBlock -> T n a -> LLVM.CodeGenFunction r (T n a)
   addPhis ::
      (TypeNum.Positive n) =>
      LLVM.BasicBlock -> T n a -> T n a -> LLVM.CodeGenFunction r ()

   shuffleMatch ::
      (TypeNum.Positive n) =>
      LLVM.ConstValue (LLVM.Vector n Word32) -> T n a -> CodeGenFunction r (T n a)
   extract ::
      (TypeNum.Positive n) =>
      LLVM.Value Word32 -> T n a -> CodeGenFunction r (MultiValue.T a)
   insert ::
      (TypeNum.Positive n) =>
      LLVM.Value Word32 -> MultiValue.T a ->
      T n a -> CodeGenFunction r (T n a)

instance C Bool where
   undef = undefPrimitive
   zero = zeroPrimitive
   phis = phisPrimitive
   addPhis = addPhisPrimitive
   shuffleMatch = shuffleMatchPrimitive
   extract = extractPrimitive
   insert = insertPrimitive

instance C Float where
   undef = undefPrimitive
   zero = zeroPrimitive
   phis = phisPrimitive
   addPhis = addPhisPrimitive
   shuffleMatch = shuffleMatchPrimitive
   extract = extractPrimitive
   insert = insertPrimitive

instance C Double where
   undef = undefPrimitive
   zero = zeroPrimitive
   phis = phisPrimitive
   addPhis = addPhisPrimitive
   shuffleMatch = shuffleMatchPrimitive
   extract = extractPrimitive
   insert = insertPrimitive

undefPrimitive ::
   (TypeNum.Positive n, IsPrimitive a,
    Repr (Value n) a ~ Value n a) =>
   T n a
undefPrimitive = Cons $ Value $ LLVM.value LLVM.undef

zeroPrimitive ::
   (TypeNum.Positive n, IsPrimitive a,
    Repr (Value n) a ~ Value n a) =>
   T n a
zeroPrimitive = Cons $ Value $ LLVM.value LLVM.zero

phisPrimitive ::
   (TypeNum.Positive n, IsPrimitive a, Repr (Value n) a ~ Value n a) =>
   LLVM.BasicBlock -> T n a -> LLVM.CodeGenFunction r (T n a)
phisPrimitive bb (Cons (Value a)) = fmap (Cons . Value) $ Loop.phis bb a

addPhisPrimitive ::
   (TypeNum.Positive n, IsPrimitive a, Repr (Value n) a ~ Value n a) =>
   LLVM.BasicBlock -> T n a -> T n a -> LLVM.CodeGenFunction r ()
addPhisPrimitive bb (Cons (Value a)) (Cons (Value b)) = Loop.addPhis bb a b


shuffleMatchPrimitive ::
   (TypeNum.Positive n, IsPrimitive a,
    Repr LLVM.Value a ~ LLVM.Value a,
    Repr (Value n) a ~ Value n a) =>
   LLVM.ConstValue (LLVM.Vector n Word32) -> T n a -> CodeGenFunction r (T n a)
shuffleMatchPrimitive k (Cons (Value v)) =
   fmap (Cons . Value) $ LLVM.shufflevector v (value LLVM.undef) k

extractPrimitive ::
   (TypeNum.Positive n, IsPrimitive a,
    Repr LLVM.Value a ~ LLVM.Value a,
    Repr (Value n) a ~ Value n a) =>
   LLVM.Value Word32 -> T n a -> CodeGenFunction r (MultiValue.T a)
extractPrimitive k (Cons (Value v)) =
   fmap MultiValue.Cons $ LLVM.extractelement v k

insertPrimitive ::
   (TypeNum.Positive n, IsPrimitive a,
-- this constraint is accepted, but does not help
--    Repr f a ~ f a,
    Repr LLVM.Value a ~ LLVM.Value a,
    Repr (Value n) a ~ Value n a) =>
   LLVM.Value Word32 ->
   MultiValue.T a -> T n a -> CodeGenFunction r (T n a)
insertPrimitive k (MultiValue.Cons a) (Cons (Value v)) =
   fmap (Cons . Value) $ LLVM.insertelement v a k


instance (C a, C b) => C (a,b) where
   undef = zip undef undef
   zero = zip zero zero

   phis bb a =
      case unzip a of
         (a0,a1) ->
            Monad.lift2 zip (phis bb a0) (phis bb a1)
   addPhis bb a b =
      case (unzip a, unzip b) of
         ((a0,a1), (b0,b1)) ->
            addPhis bb a0 b0 >>
            addPhis bb a1 b1

   shuffleMatch is v =
      case unzip v of
         (v0,v1) ->
            Monad.lift2 zip
               (shuffleMatch is v0)
               (shuffleMatch is v1)

   extract k v =
      case unzip v of
         (v0,v1) ->
            Monad.lift2 MultiValue.zip
               (extract k v0)
               (extract k v1)

   insert k a v =
      case (MultiValue.unzip a, unzip v) of
         ((a0,a1), (v0,v1)) ->
            Monad.lift2 zip
               (insert k a0 v0)
               (insert k a1 v1)


instance (C a, C b, C c) => C (a,b,c) where
   undef = zip3 undef undef undef
   zero = zip3 zero zero zero

   phis bb a =
      case unzip3 a of
         (a0,a1,a2) ->
            Monad.lift3 zip3 (phis bb a0) (phis bb a1) (phis bb a2)
   addPhis bb a b =
      case (unzip3 a, unzip3 b) of
         ((a0,a1,a2), (b0,b1,b2)) ->
            addPhis bb a0 b0 >>
            addPhis bb a1 b1 >>
            addPhis bb a2 b2

   shuffleMatch is v =
      case unzip3 v of
         (v0,v1,v2) ->
            Monad.lift3 zip3
               (shuffleMatch is v0)
               (shuffleMatch is v1)
               (shuffleMatch is v2)

   extract k v =
      case unzip3 v of
         (v0,v1,v2) ->
            Monad.lift3 MultiValue.zip3
               (extract k v0)
               (extract k v1)
               (extract k v2)

   insert k a v =
      case (MultiValue.unzip3 a, unzip3 v) of
         ((a0,a1,a2), (v0,v1,v2)) ->
            Monad.lift3 zip3
               (insert k a0 v0)
               (insert k a1 v1)
               (insert k a2 v2)


class (C a) => IntegerConstant a where
   fromInteger' :: (TypeNum.Positive n) => Integer -> T n a

class (IntegerConstant a) => RationalConstant a where
   fromRational' :: (TypeNum.Positive n) => Rational -> T n a

instance IntegerConstant Float  where fromInteger' = Cons . Value . LLVM.value . SoV.constFromInteger
instance IntegerConstant Double where fromInteger' = Cons . Value . LLVM.value . SoV.constFromInteger

instance RationalConstant Float  where fromRational' = Cons . Value . LLVM.value . SoV.constFromRational
instance RationalConstant Double where fromRational' = Cons . Value . LLVM.value . SoV.constFromRational


instance
   (TypeNum.Positive n, IntegerConstant a) =>
      A.IntegerConstant (T n a) where
   fromInteger' = fromInteger'

instance
   (TypeNum.Positive n, RationalConstant a) =>
      A.RationalConstant (T n a) where
   fromRational' = fromRational'


modify ::
   (TypeNum.Positive n, C a) =>
   LLVM.Value Word32 ->
   (MultiValue.T a -> CodeGenFunction r (MultiValue.T a)) ->
   (T n a -> CodeGenFunction r (T n a))
modify k f v =
   flip (insert k) v =<< f =<< extract k v


assemble ::
   (TypeNum.Positive n, C a) =>
   [MultiValue.T a] -> CodeGenFunction r (T n a)
assemble =
   foldM (\v (k,x) -> insert (valueOf k) x v) undef .
   List.zip [0..]

dissect ::
   (TypeNum.Positive n, C a) =>
   T n a -> LLVM.CodeGenFunction r [MultiValue.T a]
dissect = sequence . dissectList

dissectList ::
   (TypeNum.Positive n, C a) =>
   T n a -> [LLVM.CodeGenFunction r (MultiValue.T a)]
dissectList x =
   List.map
      (flip extract x . LLVM.valueOf)
      (take (size x) [0..])


map ::
   (TypeNum.Positive n, C a, C b) =>
   (MultiValue.T a -> CodeGenFunction r (MultiValue.T b)) ->
   (T n a -> CodeGenFunction r (T n b))
map f  =  assemble <=< mapM f <=< dissect


replicate ::
   (TypeNum.Positive n, C a) =>
   MultiValue.T a -> CodeGenFunction r (T n a)
replicate = replicateCore TypeNum.singleton

replicateCore ::
   (TypeNum.Positive n, C a) =>
   TypeNum.Singleton n -> MultiValue.T a -> CodeGenFunction r (T n a)
replicateCore n =
   assemble . List.replicate (TypeNum.integralFromSingleton n)


iterate ::
   (TypeNum.Positive n, C a) =>
   (MultiValue.T a -> CodeGenFunction r (MultiValue.T a)) ->
   MultiValue.T a -> CodeGenFunction r (T n a)
iterate f x =
   fmap snd $
   iterateCore f x Class.undefTuple

iterateCore ::
   (TypeNum.Positive n, C a) =>
   (MultiValue.T a -> CodeGenFunction r (MultiValue.T a)) ->
   MultiValue.T a -> T n a ->
   CodeGenFunction r (MultiValue.T a, T n a)
iterateCore f x0 v0 =
   foldM
      (\(x,v) k ->
         Monad.lift2 (,) (f x)
            (insert (valueOf k) x v))
      (x0,v0)
      (take (size v0) [0..])


-- * re-ordering of elements

constCyclicVector ::
   (LLVM.IsConst a, TypeNum.Positive n) =>
   NonEmpty.T [] a -> LLVM.ConstValue (LLVM.Vector n a)
constCyclicVector =
   LLVM.constCyclicVector . fmap LLVM.constOf

{- |
Rotate one element towards the higher elements.

I don't want to call it rotateLeft or rotateRight,
because there is no prefered layout for the vector elements.
In Intel's instruction manual vector
elements are indexed like the bits,
that is from right to left.
However, when working with Haskell list and enumeration syntax,
the start index is left.
-}
rotateUp ::
   (TypeNum.Positive n, C a) =>
   T n a -> CodeGenFunction r (T n a)
rotateUp x =
   shuffleMatch
      (constCyclicVector $
       (fromIntegral (size x) - 1) !: [0..]) x

rotateDown ::
   (TypeNum.Positive n, C a) =>
   T n a -> CodeGenFunction r (T n a)
rotateDown x =
   shuffleMatch
      (constCyclicVector $
       NonEmpty.snoc (List.take (size x - 1) [1..]) 0) x

reverse ::
   (TypeNum.Positive n, C a) =>
   T n a -> CodeGenFunction r (T n a)
reverse x =
   shuffleMatch
      (constCyclicVector $
       maybe (error "vector size must be positive") NonEmpty.reverse $
       NonEmpty.fetch $
       List.take (size x) [0..])
      x

shiftUp ::
   (TypeNum.Positive n, C a) =>
   MultiValue.T a -> T n a -> CodeGenFunction r (MultiValue.T a, T n a)
shiftUp x0 x = do
   y <-
      shuffleMatch
         (LLVM.constCyclicVector $ LLVM.undef !: List.map LLVM.constOf [0..]) x
   Monad.lift2 (,)
      (extract (LLVM.valueOf (fromIntegral (size x) - 1)) x)
      (insert (value LLVM.zero) x0 y)

shiftDown ::
   (TypeNum.Positive n, C a) =>
   MultiValue.T a -> T n a -> CodeGenFunction r (MultiValue.T a, T n a)
shiftDown x0 x = do
   y <-
      shuffleMatch
         (LLVM.constCyclicVector $
          NonEmpty.snoc
             (List.map LLVM.constOf $ List.take (size x - 1) [1..])
             LLVM.undef) x
   Monad.lift2 (,)
      (extract (value LLVM.zero) x)
      (insert (LLVM.valueOf (fromIntegral (size x) - 1)) x0 y)

shiftUpMultiZero ::
   (TypeNum.Positive n, C a, Class.ValueTuple a ~ al, Class.Zero al) =>
   Int -> T n a -> LLVM.CodeGenFunction r (T n a)
shiftUpMultiZero n v =
   assemble . take (size v) .
   (List.replicate n MultiValue.zero ++) =<< dissect v

shiftDownMultiZero ::
   (TypeNum.Positive n, C a, Class.ValueTuple a ~ al, Class.Zero al) =>
   Int -> T n a -> LLVM.CodeGenFunction r (T n a)
shiftDownMultiZero n v =
   assemble . take (size v) .
   (++ List.repeat MultiValue.zero) . List.drop n
      =<< dissect v


-- * method implementations based on Traversable

shuffleMatchTraversable ::
   (TypeNum.Positive n, C a, Trav.Traversable f) =>
   LLVM.ConstValue (LLVM.Vector n Word32) ->
   f (T n a) -> CodeGenFunction r (f (T n a))
shuffleMatchTraversable is v =
   Trav.mapM (shuffleMatch is) v

insertTraversable ::
   (TypeNum.Positive n, C a, Trav.Traversable f, App.Applicative f) =>
   LLVM.Value Word32 -> f (MultiValue.T a) ->
   f (T n a) -> CodeGenFunction r (f (T n a))
insertTraversable n a v =
   Trav.sequence (liftA2 (insert n) a v)

extractTraversable ::
   (TypeNum.Positive n, C a, Trav.Traversable f) =>
   LLVM.Value Word32 -> f (T n a) ->
   CodeGenFunction r (f (MultiValue.T a))
extractTraversable n v =
   Trav.mapM (extract n) v


type PrimValue n a = LLVM.Value (LLVM.Vector n a)


lift1 :: (Repr (Value n) a -> Repr (Value n) b) -> T n a -> T n b
lift1 f (Cons a) = Cons $ f a

_liftM0 ::
   (Monad m) =>
   m (Repr (Value n) a) ->
   m (T n a)
_liftM0 f = Monad.lift Cons f

liftM0 ::
   (Monad m,
    Repr (Value n) a ~ Value n a) =>
   m (PrimValue n a) ->
   m (T n a)
liftM0 f = Monad.lift consPrim f

liftM ::
   (Monad m,
    Repr (Value n) a ~ Value n a,
    Repr (Value n) b ~ Value n b) =>
   (PrimValue n a -> m (PrimValue n b)) ->
   T n a -> m (T n b)
liftM f a = Monad.lift consPrim $ f (deconsPrim a)

liftM2 ::
   (Monad m,
    Repr (Value n) a ~ Value n a,
    Repr (Value n) b ~ Value n b,
    Repr (Value n) c ~ Value n c) =>
   (PrimValue n a -> PrimValue n b -> m (PrimValue n c)) ->
   T n a -> T n b -> m (T n c)
liftM2 f a b = Monad.lift consPrim $ f (deconsPrim a) (deconsPrim b)



class (MultiValue.Additive a, C a) => Additive a where
   add ::
      (TypeNum.Positive n) =>
      T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   sub ::
      (TypeNum.Positive n) =>
      T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   neg ::
      (TypeNum.Positive n) =>
      T n a -> LLVM.CodeGenFunction r (T n a)

instance Additive Float where
   add = liftM2 LLVM.add
   sub = liftM2 LLVM.sub
   neg = liftM LLVM.neg

instance Additive Double where
   add = liftM2 LLVM.add
   sub = liftM2 LLVM.sub
   neg = liftM LLVM.neg

instance (TypeNum.Positive n, Additive a) => A.Additive (T n a) where
   zero = zero
   add = add
   sub = sub
   neg = neg


class (MultiValue.PseudoRing a, Additive a) => PseudoRing a where
   mul ::
      (TypeNum.Positive n) =>
      T n a -> T n a -> LLVM.CodeGenFunction r (T n a)

instance PseudoRing Float where
   mul = liftM2 LLVM.mul

instance PseudoRing Double where
   mul = liftM2 LLVM.mul

instance (TypeNum.Positive n, PseudoRing a) => A.PseudoRing (T n a) where
   mul = mul


class (MultiValue.Field a, PseudoRing a) => Field a where
   fdiv ::
      (TypeNum.Positive n) =>
      T n a -> T n a -> LLVM.CodeGenFunction r (T n a)

instance Field Float where
   fdiv = liftM2 LLVM.fdiv

instance Field Double where
   fdiv = liftM2 LLVM.fdiv

instance (TypeNum.Positive n, Field a) => A.Field (T n a) where
   fdiv = fdiv


type instance A.Scalar (T n a) = T n (MultiValue.Scalar a)

class
   (MultiValue.PseudoModule v, PseudoRing (MultiValue.Scalar v), Additive v) =>
      PseudoModule v where
   scale ::
      (TypeNum.Positive n) =>
      T n (MultiValue.Scalar v) -> T n v -> LLVM.CodeGenFunction r (T n v)

instance PseudoModule Float where
   scale = liftM2 A.mul

instance PseudoModule Double where
   scale = liftM2 A.mul

instance (TypeNum.Positive n, PseudoModule a) => A.PseudoModule (T n a) where
   scale = scale


class (MultiValue.Real a, Additive a) => Real a where
   min :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   max :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   abs :: (TypeNum.Positive n) => T n a -> LLVM.CodeGenFunction r (T n a)
   signum :: (TypeNum.Positive n) => T n a -> LLVM.CodeGenFunction r (T n a)

instance Real Float where
   min = liftM2 A.min
   max = liftM2 A.max
   abs = liftM A.abs
   signum = liftM A.signum

instance Real Double where
   min = liftM2 A.min
   max = liftM2 A.max
   abs = liftM A.abs
   signum = liftM A.signum

instance (TypeNum.Positive n, Real a) => A.Real (T n a) where
   min = min
   max = max
   abs = abs
   signum = signum


class (MultiValue.Fraction a, Real a) => Fraction a where
   truncate :: (TypeNum.Positive n) => T n a -> LLVM.CodeGenFunction r (T n a)
   fraction :: (TypeNum.Positive n) => T n a -> LLVM.CodeGenFunction r (T n a)

instance Fraction Float where
   truncate = liftM A.truncate
   fraction = liftM A.fraction

instance Fraction Double where
   truncate = liftM A.truncate
   fraction = liftM A.fraction

instance (TypeNum.Positive n, Fraction a) => A.Fraction (T n a) where
   truncate = truncate
   fraction = fraction


class (MultiValue.Algebraic a, Field a) => Algebraic a where
   sqrt :: (TypeNum.Positive n) => T n a -> LLVM.CodeGenFunction r (T n a)

instance Algebraic Float where
   sqrt = liftM A.sqrt

instance Algebraic Double where
   sqrt = liftM A.sqrt

instance (TypeNum.Positive n, Algebraic a) => A.Algebraic (T n a) where
   sqrt = sqrt


class (MultiValue.Transcendental a, Algebraic a) => Transcendental a where
   pi :: (TypeNum.Positive n) => LLVM.CodeGenFunction r (T n a)
   sin, cos, exp, log ::
      (TypeNum.Positive n) => T n a -> LLVM.CodeGenFunction r (T n a)
   pow :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)

instance Transcendental Float where
   pi = liftM0 A.pi
   sin = liftM A.sin
   cos = liftM A.cos
   exp = liftM A.exp
   log = liftM A.log
   pow = liftM2 A.pow

instance Transcendental Double where
   pi = liftM0 A.pi
   sin = liftM A.sin
   cos = liftM A.cos
   exp = liftM A.exp
   log = liftM A.log
   pow = liftM2 A.pow

instance (TypeNum.Positive n, Transcendental a) => A.Transcendental (T n a) where
   pi = pi
   sin = sin
   cos = cos
   exp = exp
   log = log
   pow = pow



class (MultiValue.Comparison a, C a) => Comparison a where
   cmp ::
      (TypeNum.Positive n) =>
      LLVM.CmpPredicate -> T n a -> T n a ->
      LLVM.CodeGenFunction r (T n Bool)

instance Comparison Float where
   cmp = liftM2 . LLVM.cmp

instance Comparison Double where
   cmp = liftM2 . LLVM.cmp

instance (TypeNum.Positive n, Comparison a) => A.Comparison (T n a) where
   type CmpResult (T n a) = T n Bool
   cmp = cmp



class
   (MultiValue.FloatingComparison a, Comparison a) =>
      FloatingComparison a where
   fcmp ::
      (TypeNum.Positive n) =>
      LLVM.FPPredicate -> T n a -> T n a ->
      LLVM.CodeGenFunction r (T n Bool)

instance FloatingComparison Float where
   fcmp = liftM2 . LLVM.fcmp

instance
   (TypeNum.Positive n, FloatingComparison a) =>
      A.FloatingComparison (T n a) where
   fcmp = fcmp



class (MultiValue.Logic a, C a) => Logic a where
   and :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   or :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   xor :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   inv :: (TypeNum.Positive n) => T n a -> LLVM.CodeGenFunction r (T n a)

instance Logic Bool where
   and = liftM2 LLVM.and
   or = liftM2 LLVM.or
   xor = liftM2 LLVM.xor
   inv = liftM LLVM.inv


instance (TypeNum.Positive n, Logic a) => A.Logic (T n a) where
   and = and
   or = or
   xor = xor
   inv = inv



class BitShift a where
   shl :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)
   shr :: (TypeNum.Positive n) => T n a -> T n a -> LLVM.CodeGenFunction r (T n a)

instance BitShift Word8 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.lshr

instance BitShift Word16 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.lshr

instance BitShift Word32 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.lshr

instance BitShift Word64 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.lshr

instance BitShift Int8 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.ashr

instance BitShift Int16 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.ashr

instance BitShift Int32 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.ashr

instance BitShift Int64 where
   shl = liftM2 LLVM.shl; shr = liftM2 LLVM.ashr