--------------------------------------------------------------------------------
-- Copyright © 2011 National Institute of Aerospace / Galois, Inc.
--------------------------------------------------------------------------------

-- | Integral class operators.

{-# LANGUAGE Safe #-}

module Copilot.Language.Operators.Integral
  ( div
  , mod
  , (^)
  ) where

import Copilot.Core (Typed, typeOf)
import qualified Copilot.Core as Core
import Copilot.Language.Operators.BitWise ((.<<.))
import Copilot.Language.Stream

import qualified Data.Bits as B
import qualified Prelude as P
import Data.List (foldl', replicate)

--------------------------------------------------------------------------------

div :: (Typed a, P.Integral a) => Stream a -> Stream a -> Stream a
(Const a
0) div :: Stream a -> Stream a -> Stream a
`div` Stream a
_ = a -> Stream a
forall a. Typed a => a -> Stream a
Const a
0
Stream a
_ `div` (Const a
0) = String -> Stream a
forall a. String -> a
Core.badUsage String
"in div: division by zero."
Stream a
x `div` (Const a
1) = Stream a
x
Stream a
x `div` Stream a
y = Op2 a a a -> Stream a -> Stream a -> Stream a
forall a b c.
(Typed a, Typed b, Typed c) =>
Op2 a b c -> Stream a -> Stream b -> Stream c
Op2 (Type a -> Op2 a a a
forall a. Integral a => Type a -> Op2 a a a
Core.Div Type a
forall a. Typed a => Type a
typeOf) Stream a
x Stream a
y

mod :: (Typed a, P.Integral a) => Stream a -> Stream a -> Stream a
Stream a
_         mod :: Stream a -> Stream a -> Stream a
`mod` (Const a
0) = String -> Stream a
forall a. String -> a
Core.badUsage String
"in mod: division by zero."
(Const a
0) `mod` Stream a
_         = (a -> Stream a
forall a. Typed a => a -> Stream a
Const a
0)
(Const a
x) `mod` (Const a
y) = a -> Stream a
forall a. Typed a => a -> Stream a
Const (a
x a -> a -> a
forall a. Integral a => a -> a -> a
`P.mod` a
y)
Stream a
x `mod` Stream a
y = Op2 a a a -> Stream a -> Stream a -> Stream a
forall a b c.
(Typed a, Typed b, Typed c) =>
Op2 a b c -> Stream a -> Stream b -> Stream c
Op2 (Type a -> Op2 a a a
forall a. Integral a => Type a -> Op2 a a a
Core.Mod Type a
forall a. Typed a => Type a
typeOf) Stream a
x Stream a
y

(^) :: (Typed a, Typed b, P.Num a, B.Bits a, P.Integral b)
    => Stream a -> Stream b -> Stream a
(Const a
0) ^ :: Stream a -> Stream b -> Stream a
^ (Const b
0)  = a -> Stream a
forall a. Typed a => a -> Stream a
Const a
1
(Const a
0) ^ Stream b
x          = Op3 Bool a a a -> Stream Bool -> Stream a -> Stream a -> Stream a
forall a b c d.
(Typed a, Typed b, Typed c, Typed d) =>
Op3 a b c d -> Stream a -> Stream b -> Stream c -> Stream d
Op3 (Type a -> Op3 Bool a a a
forall b. Type b -> Op3 Bool b b b
Core.Mux Type a
forall a. Typed a => Type a
typeOf) (Op2 b b Bool -> Stream b -> Stream b -> Stream Bool
forall a b c.
(Typed a, Typed b, Typed c) =>
Op2 a b c -> Stream a -> Stream b -> Stream c
Op2 (Type b -> Op2 b b Bool
forall a. Eq a => Type a -> Op2 a a Bool
Core.Eq Type b
forall a. Typed a => Type a
typeOf) Stream b
x Stream b
0) (Stream a
1) (Stream a
0)
(Const a
1) ^ Stream b
_          = a -> Stream a
forall a. Typed a => a -> Stream a
Const a
1
(Const a
x) ^ (Const b
y)  = a -> Stream a
forall a. Typed a => a -> Stream a
Const (a
x a -> b -> a
forall a b. (Num a, Integral b) => a -> b -> a
P.^ b
y)
(Const a
2) ^ Stream b
y          = (a -> Stream a
forall a. Typed a => a -> Stream a
Const a
1) Stream a -> Stream b -> Stream a
forall a b.
(Bits a, Typed a, Typed b, Integral b) =>
Stream a -> Stream b -> Stream a
.<<. Stream b
y
Stream a
x ^ (Const b
y)          = (Stream a -> Stream a -> Stream a)
-> Stream a -> [Stream a] -> Stream a
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Stream a -> Stream a -> Stream a
forall a. Num a => a -> a -> a
(P.*)) (a -> Stream a
forall a. Typed a => a -> Stream a
Const a
1) (Int -> Stream a -> [Stream a]
forall a. Int -> a -> [a]
replicate (b -> Int
forall a b. (Integral a, Num b) => a -> b
P.fromIntegral b
y) Stream a
x)
Stream a
_ ^ Stream b
_                  = String -> Stream a
forall a. String -> a
Core.badUsage String
"in ^: in x ^ y, either x must be the constant 2, or y must be a constant.  (Do not confuse ^ with bitwise XOR (.^.) or with ** for exponentation of floats/doubles.)"

--------------------------------------------------------------------------------