module Ideas.Common.Algebra.Field
(
SemiRing(..)
, Ring(..)
, Field(..)
, Additive(..)
, Multiplicative(..)
, SafeNum, safeNum
, CoSemiRing(..), CoRing(..), CoField(..)
) where
import Control.Monad
import Ideas.Common.Algebra.Group
import Ideas.Common.Classes (mapBoth)
import Test.QuickCheck
import qualified Control.Applicative as A
infixl 6 <+>
infixl 7 <*>
class SemiRing a where
(<+>) :: a -> a -> a
zero :: a
sum :: [a] -> a
(<*>) :: a -> a -> a
one :: a
product :: [a] -> a
sum [] = zero
sum xs = foldl1 (<+>) xs
product [] = one
product xs = foldl1 (<*>) xs
infixl 6 <->
class SemiRing a => Ring a where
plusInverse :: a -> a
(<->) :: a -> a -> a
plusInverse = (zero <->)
a <-> b = a <+> plusInverse b
infixl 7 </>
class Ring a => Field a where
timesInverse :: a -> a
(</>) :: a -> a -> a
timesInverse = (one </>)
a </> b = a <*> timesInverse b
newtype Additive a = Additive {fromAdditive :: a}
deriving (Show, Eq, Ord, Arbitrary, CoArbitrary)
instance Functor Additive where
fmap f = Additive . f . fromAdditive
instance A.Applicative Additive where
pure = Additive
Additive f <*> Additive a = Additive (f a)
instance SemiRing a => Monoid (Additive a) where
mempty = A.pure zero
mappend = A.liftA2 (<+>)
instance Ring a => Group (Additive a) where
inverse = A.liftA plusInverse
appendInv = A.liftA2 (<->)
newtype Multiplicative a = Multiplicative {fromMultiplicative :: a}
deriving (Show, Eq, Ord, Arbitrary, CoArbitrary)
instance Functor Multiplicative where
fmap f = Multiplicative . f . fromMultiplicative
instance A.Applicative Multiplicative where
pure = Multiplicative
Multiplicative f <*> Multiplicative a = Multiplicative (f a)
instance SemiRing a => Monoid (Multiplicative a) where
mempty = A.pure one
mappend = A.liftA2 (<*>)
instance Field a => Group (Multiplicative a) where
inverse = A.liftA timesInverse
appendInv = A.liftA2 (</>)
instance SemiRing a => MonoidZero (Multiplicative a) where
mzero = Multiplicative zero
data SafeNum a = Ok a | Exception String
safeNum :: SafeNum a -> Either String a
safeNum (Ok a) = Right a
safeNum (Exception s) = Left s
instance Arbitrary a => Arbitrary (SafeNum a) where
arbitrary = liftM return arbitrary
instance Eq a => Eq (SafeNum a) where
Ok a == Ok b = a == b
_ == _ = True
instance Ord a => Ord (SafeNum a) where
Ok a `compare` Ok b = a `compare` b
_ `compare` _ = EQ
instance Show a => Show (SafeNum a) where
show = either ("Exception: " ++) show . safeNum
instance Functor SafeNum where
fmap f = either Exception (return . f) . safeNum
instance Monad SafeNum where
return = Ok
fail = Exception
m >>= f = either Exception f (safeNum m)
instance Num a => Num (SafeNum a) where
(+) = liftM2 (+)
(*) = liftM2 (*)
() = liftM2 ()
negate = liftM negate
abs = liftM abs
signum = liftM signum
fromInteger = return . fromInteger
instance (Eq a, Fractional a) => Fractional (SafeNum a) where
a / b = liftM2 (/) a (safeDivisor b)
recip = liftM recip . safeDivisor
fromRational = return . fromRational
instance Num a => SemiRing (SafeNum a) where
(<+>) = (+)
(<*>) = (*)
zero = 0
one = 1
instance Num a => Ring (SafeNum a) where
plusInverse = negate
(<->) = ()
instance (Eq a, Fractional a) => Field (SafeNum a) where
timesInverse = recip
(</>) = (/)
safeDivisor :: (Eq a, Num a) => SafeNum a -> SafeNum a
safeDivisor m = m >>= \a ->
if a == 0 then fail "division by zero" else return a
class CoSemiRing a where
isPlus :: a -> Maybe (a, a)
isZero :: a -> Bool
isTimes :: a -> Maybe (a, a)
isOne :: a -> Bool
class CoSemiRing a => CoRing a where
isNegate :: a -> Maybe a
isMinus :: a -> Maybe (a, a)
isMinus _ = Nothing
class CoRing a => CoField a where
isRecip :: a -> Maybe a
isDivision :: a -> Maybe (a, a)
isDivision _ = Nothing
instance CoSemiRing a => CoMonoid (Additive a) where
isEmpty = isZero . fromAdditive
isAppend = fmap (mapBoth Additive) . isPlus . fromAdditive
instance CoRing a => CoGroup (Additive a) where
isInverse = fmap Additive . isNegate . fromAdditive
isAppendInv = fmap (mapBoth Additive) . isMinus . fromAdditive
instance CoSemiRing a => CoMonoid (Multiplicative a) where
isEmpty = isOne . fromMultiplicative
isAppend = fmap (mapBoth Multiplicative) . isTimes . fromMultiplicative
instance CoField a => CoGroup (Multiplicative a) where
isInverse = fmap Multiplicative . isRecip . fromMultiplicative
isAppendInv = fmap (mapBoth Multiplicative) . isDivision . fromMultiplicative
instance CoSemiRing a => CoMonoidZero (Multiplicative a) where
isMonoidZero = isZero . fromMultiplicative