{-# Language ConstraintKinds #-}
{-# Language Rank2Types #-}
module Data.Dioid.Signed where
import Data.Bifunctor (first)
import Data.Connection
import Data.Connection.Float
import Data.Float
import Data.Ord (Down(..))
import Data.Prd
import Data.Prd.Lattice
import Data.Semigroup.Quantale
import Data.Semiring
import Prelude
data Sign = Zero | Negative | Positive | Indeterminate deriving (Show, Eq)
signOf :: (Eq a, Num a, Prd a) => a -> Sign
signOf x = case sign x of
Nothing -> Indeterminate
Just EQ -> Zero
Just LT -> Negative
Just GT -> Positive
instance Semigroup Sign where
Positive <> Positive = Positive
Positive <> Negative = Indeterminate
Positive <> Zero = Positive
Positive <> Indeterminate = Indeterminate
Negative <> Positive = Indeterminate
Negative <> Negative = Negative
Negative <> Zero = Negative
Negative <> Indeterminate = Indeterminate
Zero <> a = a
Indeterminate <> _ = Indeterminate
instance Monoid Sign where
mempty = Zero
instance Semiring Sign where
Positive >< a = a
Negative >< Positive = Negative
Negative >< Negative = Positive
Negative >< Zero = Zero
Negative >< Indeterminate = Indeterminate
Zero >< _ = Zero
Indeterminate >< Zero = Zero
Indeterminate >< _ = Indeterminate
fromBoolean = fromBooleanDef Positive
instance Prd Sign where
Positive <~ Positive = True
Positive <~ Negative = False
Positive <~ Zero = False
Positive <~ Indeterminate = True
Negative <~ Positive = False
Negative <~ Negative = True
Negative <~ Zero = False
Negative <~ Indeterminate = True
Zero <~ _ = True
Indeterminate <~ Indeterminate = True
Indeterminate <~ _ = False
instance Min Sign where
minimal = Zero
instance Max Sign where
maximal = Indeterminate
instance Bounded Sign where
minBound = minimal
maxBound = maximal
newtype Signed = Signed { unSigned :: Float }
instance Show Signed where
show (Signed x) = show x
instance Eq Signed where
(Signed x) == (Signed y) | isNan x && isNan y = True
| isNan x || isNan y = False
| otherwise = split x == split y
instance Prd Signed where
Signed x <~ Signed y | isNan x && isNan y = True
| isNan x || isNan y = False
| otherwise = (first Down $ split x) <~ (first Down $ split y)
pcompare (Signed x) (Signed y) | isNan x && isNan y = Just EQ
| isNan x || isNan y = Nothing
| otherwise = pcompare (first Down $ split x) (first Down $ split y)
f32sgn :: Conn Float Signed
f32sgn = Conn f g where
f x | x == nInf = Signed $ -0
| otherwise = Signed $ either (const 0) id $ split x
g (Signed x) = either (const nInf) id $ split x
ugnsgn :: Conn Unsigned Signed
ugnsgn = Conn f g where
f (Unsigned x) = Signed $ abs x
g (Signed x) = Unsigned $ either (const 0) id $ split x
newtype Unsigned = Unsigned Float
unsigned :: Signed -> Unsigned
unsigned (Signed x) = Unsigned (abs x)
instance Show Unsigned where
show (Unsigned x) = show $ abs x
instance Eq Unsigned where
(Unsigned x) == (Unsigned y) | finite x && finite y = (abs x) == (abs y)
| not (finite x) && not (finite y) = True
| otherwise = False
instance Prd Unsigned where
u <~ v = u `ltugn` v || u == v
ltugn :: Unsigned -> Unsigned -> Bool
ltugn (Unsigned x) (Unsigned y) | finite x && finite y = (abs x) < shift (-2) (abs y)
| finite x && not (finite y) = True
| otherwise = False
instance Min Unsigned where
minimal = Unsigned 0
instance Max Unsigned where
maximal = Unsigned pInf
instance Lattice Unsigned where
(Unsigned x) \/ (Unsigned y) | finite x && finite y = Unsigned $ max (abs x) (abs y)
| otherwise = Unsigned x
(Unsigned x) /\ (Unsigned y) | finite x && finite y = Unsigned $ min (abs x) (abs y)
| not (finite x) && finite y = Unsigned y
| otherwise = Unsigned x
instance Semigroup Unsigned where
Unsigned x <> Unsigned y = Unsigned $ abs x + abs y
instance Monoid Unsigned where
mempty = Unsigned 0
instance Semiring Unsigned where
Unsigned x >< Unsigned y | zero x || zero y = Unsigned 0
| otherwise = Unsigned $ abs x * abs y
fromBoolean = fromBooleanDef (Unsigned 1)
instance Quantale Unsigned where
x \\ y = y // x
Unsigned y // Unsigned x = Unsigned . max 0 $ y // x