{-# OPTIONS_GHC -Wall #-} -- | Field classes module Plankton.Field ( Semifield , Field , ExpField(..) , QuotientField(..) , BoundedField(..) , infinity , neginfinity , TrigField(..) ) where import Data.Complex (Complex(..)) import Plankton.Additive import Plankton.Multiplicative import Plankton.Ring import Protolude (Bool, Double, Float, Integer, (||)) import qualified Protolude as P -- | A Semifield is a Field without Commutative Multiplication. class (MultiplicativeInvertible a, Ring a) => Semifield a instance Semifield Double instance Semifield Float instance (Semifield a) => Semifield (Complex a) -- | A Field is a Ring plus additive invertible and multiplicative invertible operations. -- -- A summary of the rules inherited from super-classes of Field -- -- > zero + a == a -- > a + zero == a -- > (a + b) + c == a + (b + c) -- > a + b == b + a -- > a - a = zero -- > negate a = zero - a -- > negate a + a = zero -- > a + negate a = zero -- > one * a == a -- > a * one == a -- > (a * b) * c == a * (b * c) -- > a * (b + c) == a * b + a * c -- > (a + b) * c == a * c + b * c -- > a * zero == zero -- > zero * a == zero -- > a * b == b * a -- > a / a = one -- > recip a = one / a -- > recip a * a = one -- > a * recip a = one class (AdditiveGroup a, MultiplicativeGroup a, Ring a) => Field a instance Field Double instance Field Float instance (Field a) => Field (Complex a) -- | A hyperbolic field class -- -- > sqrt . (**2) == identity -- > log . exp == identity -- > for +ive b, a != 0,1: a ** logBase a b ≈ b class (Field a) => ExpField a where exp :: a -> a log :: a -> a logBase :: a -> a -> a logBase a b = log b / log a (**) :: a -> a -> a (**) a b = exp (log a * b) sqrt :: a -> a sqrt a = a ** (one / (one + one)) instance ExpField Double where exp = P.exp log = P.log (**) = (P.**) instance ExpField Float where exp = P.exp log = P.log (**) = (P.**) -- | todo: bottom is here somewhere??? instance (TrigField a, ExpField a) => ExpField (Complex a) where exp (rx :+ ix) = exp rx * cos ix :+ exp rx * sin ix log (rx :+ ix) = log (sqrt (rx * rx + ix * ix)) :+ atan2 ix rx -- | quotient fields explode constraints if they allow for polymorphic integral types -- -- > a - one < floor a <= a <= ceiling a < a + one -- > round a == floor (a + one/(one+one)) class (Field a) => QuotientField a where round :: a -> Integer ceiling :: a -> Integer floor :: a -> Integer (^^) :: a -> Integer -> a instance QuotientField Float where round = P.round ceiling = P.ceiling floor = P.floor (^^) = (P.^^) instance QuotientField Double where round = P.round ceiling = P.ceiling floor = P.floor (^^) = (P.^^) -- | A bounded field includes the concepts of infinity and NaN, thus moving away from error throwing. -- -- > one / zero + infinity == infinity -- > infinity + a == infinity -- > isNaN (infinity - infinity) -- > isNaN (infinity / infinity) -- > isNaN (nan + a) -- > zero / zero != nan -- -- Note the tricky law that, although nan is assigned to zero/zero, they are never-the-less not equal. A committee decided this. class (Field a) => BoundedField a where maxBound :: a maxBound = one / zero minBound :: a minBound = negate (one / zero) nan :: a nan = zero / zero isNaN :: a -> Bool -- | prints as `Infinity` infinity :: BoundedField a => a infinity = maxBound -- | prints as `-Infinity` neginfinity :: BoundedField a => a neginfinity = minBound instance BoundedField Float where isNaN = P.isNaN instance BoundedField Double where isNaN = P.isNaN -- | todo: work out boundings for complex -- as it stands now, complex is different eg -- -- > one / (zero :: Complex Float) == nan instance (BoundedField a) => BoundedField (Complex a) where isNaN (rx :+ ix) = isNaN rx || isNaN ix -- | Trigonometric Field class (P.Ord a, Field a) => TrigField a where pi :: a sin :: a -> a cos :: a -> a tan :: a -> a tan x = sin x / cos x asin :: a -> a acos :: a -> a atan :: a -> a sinh :: a -> a cosh :: a -> a tanh :: a -> a tanh x = sinh x / cosh x asinh :: a -> a acosh :: a -> a atanh :: a -> a atan2 :: a -> a -> a atan2 y x | x P.> zero = atan (y / x) | x P.== zero P.&& y P.> zero = pi / (one + one) | x P.< one P.&& y P.> one = pi + atan (y / x) | (x P.<= zero P.&& y P.< zero) || (x P.< zero) = negate (atan2 (negate y) x) | y P.== zero = pi -- must be after the previous test on zero y | x P.== zero P.&& y P.== zero = y -- must be after the other double zero tests | P.otherwise = x + y -- x or y is a NaN, return a NaN (via +) instance TrigField Double where pi = P.pi sin = P.sin cos = P.cos asin = P.asin acos = P.acos atan = P.atan sinh = P.sinh cosh = P.cosh asinh = P.sinh acosh = P.acosh atanh = P.atanh instance TrigField Float where pi = P.pi sin = P.sin cos = P.cos asin = P.asin acos = P.acos atan = P.atan sinh = P.sinh cosh = P.cosh asinh = P.sinh acosh = P.acosh atanh = P.atanh