{-# Language AllowAmbiguousTypes #-}
module Data.Connection.Round (
Mode(..)
, half
, tied
, above
, below
, ceilingOn
, floorOn
, roundOn
, truncOn
, addOn
, negOn
, subOn
, mulOn
, fmaOn
, remOn
, divOn
, divOn'
) where
import Data.Bool
import Data.Connection.Conn
import Data.Connection.Ratio
import Data.Float
import Data.Int
import Data.Prd
import Data.Prd.Property (xor)
import Data.Prd.Top
import Data.Ratio
import Prelude hiding (until, Ord(..), Bounded)
data Mode =
RNZ
| RTP
| RTN
| RTZ
deriving (Eq, Show)
half :: Prd a => Prd b => Num a => Trip a b -> a -> Maybe Ordering
half t x = pcompare (x - unitl t x) (counitr t x - x)
above :: Prd a => Prd b => Num a => Trip a b -> a -> Bool
above t = maybe False (== GT) . half t
below :: Prd a => Prd b => Num a => Trip a b -> a -> Bool
below t = maybe False (== LT) . half t
tied :: Prd a => Prd b => Num a => Trip a b -> a -> Bool
tied t = maybe False (== EQ) . half t
addOn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> b
addOn t@(Trip _ f _) rm x y = rnd t rm (addSgn t rm x y) (f x + f y)
negOn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b
negOn t@(Trip _ f _) rm x = rnd t rm (neg' t rm x) (0 - f x)
subOn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> b
subOn t@(Trip _ f _) rm x y = rnd t rm (subSgn t rm x y) (f x - f y)
mulOn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> b
mulOn t@(Trip _ f _) rm x y = rnd t rm (xorSgn t rm x y) (f x * f y)
fmaOn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> b -> b
fmaOn t@(Trip _ f _) rm x y z = rnd t rm (fmaSgn t rm x y z) $ f x * f y + f z
remOn :: (Prd a, Prd b, Fractional a) => Trip a b -> Mode -> b -> b -> b
remOn t rm x y = fmaOn t rm (negOn t rm $ divOn t rm x y) y x
divOn :: (Prd a, Prd b, Fractional a) => Trip a b -> Mode -> b -> b -> b
divOn t@(Trip _ f _) rm x y = rnd t rm (xorSgn t rm x y) (f x / f y)
divOn' :: (Prd a, Prd b, Fractional a) => Trip a b -> Mode -> b -> b -> b
divOn' t@(Trip _ f _) rm x y | xorSgn t rm x y = rnd t rm True (negate $ f x / f y)
| otherwise = rnd t rm False (f x / f y)
truncOn :: (Prd a, Prd b, Num a) => Trip a b -> a -> b
truncOn t x = bool (ceilingOn t x) (floorOn t x) $ x >= 0
ceilingOn :: Prd a => Prd b => Trip a b -> a -> b
ceilingOn = connl . tripl
floorOn :: Prd a => Prd b => Trip a b -> a -> b
floorOn = connr . tripr
roundOn :: (Prd a, Prd b, Num a) => Trip a b -> a -> b
roundOn t x | above t x = ceilingOn t x
| below t x = floorOn t x
| otherwise = truncOn t x
rsz :: (Prd a, Prd b) => Trip a b -> Bool -> a -> b
rsz t = bool (floorOn t) (ceilingOn t)
rnd :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> Bool -> a -> b
rnd t RNZ s x = bool (roundOn t x) (rsz t s x) $ x =~ 0
rnd t RTP s x = bool (ceilingOn t x) (rsz t s x) $ x =~ 0
rnd t RTN s x = bool (floorOn t x) (rsz t s x) $ x =~ 0
rnd t RTZ s x = bool (truncOn t x) (rsz t s x) $ x =~ 0
neg' :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> Bool
neg' t rm x = x < rnd t rm False 0
addSgn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> Bool
addSgn t rm x y | rm == RTN = neg' t rm x || neg' t rm y
| otherwise = neg' t rm x && neg' t rm y
subSgn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> Bool
subSgn t rm x y = not (addSgn t rm x y)
xorSgn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> Bool
xorSgn t rm x y = neg' t rm x `xor` neg' t rm y
fmaSgn :: (Prd a, Prd b, Num a) => Trip a b -> Mode -> b -> b -> b -> Bool
fmaSgn t rm x y z = addSgn t rm (mulOn t rm x y) z