{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# OPTIONS_GHC -Wall #-} -- | Metric classes module Plankton.Metric ( Signed(..) , Normed(..) , Metric(..) , Epsilon(..) , (≈) ) where import Data.Complex (Complex(..)) import Plankton.Additive import Plankton.Field import Plankton.Multiplicative import qualified Protolude as P import Protolude (Bool(..), Double, Eq(..), Float, Int, Integer, Ord(..), ($), (&&)) -- | 'signum' from base is not an operator replicated in numhask, being such a very silly name, and preferred is the much more obvious 'sign'. Compare with 'Norm' and 'Banach' where there is a change in codomain -- -- > abs a * sign a == a -- -- Generalising this class tends towards size and direction (abs is the size on the one-dim number line of a vector with its tail at zero, and sign is the direction, right?). class (MultiplicativeUnital a) => Signed a where sign :: a -> a abs :: a -> a instance Signed Double where sign a = if a >= zero then one else negate one abs = P.abs instance Signed Float where sign a = if a >= zero then one else negate one abs = P.abs instance Signed Int where sign a = if a >= zero then one else negate one abs = P.abs instance Signed Integer where sign a = if a >= zero then one else negate one abs = P.abs -- | Like Signed, except the codomain can be different to the domain. class Normed a b where size :: a -> b instance Normed Double Double where size = P.abs instance Normed Float Float where size = P.abs instance Normed Int Int where size = P.abs instance Normed Integer Integer where size = P.abs instance (Multiplicative a, ExpField a, Normed a a) => Normed (Complex a) a where size (rx :+ ix) = sqrt (rx * rx + ix * ix) -- | distance between numbers -- -- > distance a b >= zero -- > distance a a == zero -- > \a b c -> distance a c + distance b c - distance a b >= zero && -- > distance a b + distance b c - distance a c >= zero && -- > distance a b + distance a c - distance b c >= zero && class Metric a b where distance :: a -> a -> b instance Metric Double Double where distance a b = abs (a - b) instance Metric Float Float where distance a b = abs (a - b) instance Metric Int Int where distance a b = abs (a - b) instance Metric Integer Integer where distance a b = abs (a - b) instance (Multiplicative a, ExpField a, Normed a a) => Metric (Complex a) a where distance a b = size (a - b) -- | todo: This should probably be split off into some sort of alternative Equality logic, but to what end? class (AdditiveGroup a) => Epsilon a where nearZero :: a -> Bool aboutEqual :: a -> a -> Bool positive :: (Eq a, Signed a) => a -> Bool positive a = a == abs a veryPositive :: (Eq a, Signed a) => a -> Bool veryPositive a = P.not (nearZero a) && positive a veryNegative :: (Eq a, Signed a) => a -> Bool veryNegative a = P.not (nearZero a P.|| positive a) infixl 4 ≈ -- | todo: is utf perfectly acceptable these days? (≈) :: (Epsilon a) => a -> a -> Bool (≈) = aboutEqual instance Epsilon Double where nearZero a = abs a <= (1e-12 :: Double) aboutEqual a b = nearZero $ a - b instance Epsilon Float where nearZero a = abs a <= (1e-6 :: Float) aboutEqual a b = nearZero $ a - b instance Epsilon Int where nearZero a = a == zero aboutEqual a b = nearZero $ a - b instance Epsilon Integer where nearZero a = a == zero aboutEqual a b = nearZero $ a - b instance (Epsilon a) => Epsilon (Complex a) where nearZero (rx :+ ix) = nearZero rx && nearZero ix aboutEqual a b = nearZero $ a - b