module MagicHaskeller.NearEq where
import MagicHaskeller.FastRatio

infix 4 ~=   -- same as (==)

-- the nearly-equal operator with pattern-matching-like behavior over NaN and Infinity.
class NearEq a where
  (~=) :: a -> a -> Bool

-- Without check with isInfinite, Infinity ~= something would always be True. Without check with signum, Infinity ~= -Infinity would always be True.
instance NearEq Double where
         a ~= b = na==nb && ia == isInfinite b && signum a == signum b && (na || ia || abs (a-b) <= (scaleFloat (-24) $ abs a))
              where na = isNaN a
                    nb = isNaN b
                    ia = isInfinite a
instance NearEq Float where
         a ~= b = na==nb && ia == isInfinite b && signum a == signum b && (na || ia || abs (a-b) <= (scaleFloat (-12) $ abs a))
              where na = isNaN a
                    nb = isNaN b
                    ia = isInfinite a
instance NearEq () where
  x ~= y = x == y
instance NearEq Bool where
  x ~= y = x == y
instance NearEq Ordering where
  x ~= y = x == y
instance NearEq Char where
  x ~= y = x == y
instance NearEq Int where
  x ~= y = x == y
instance NearEq Integer where
  x ~= y = x == y
instance (NearEq i, Integral i) => NearEq (Ratio i) where
  x ~= y = numerator x ~= numerator y && denominator x ~= denominator y
instance NearEq a => NearEq [a] where
  []   ~= []   = True
  x:xs ~= y:ys = x ~= y && xs ~= ys
  _    ~= _    = False
instance NearEq a => NearEq (Maybe a) where
  Nothing ~= Nothing = True
  Just x  ~= Just y  = x ~= y
  _       ~= _       = False
instance (NearEq a, NearEq b) => NearEq (Either a b) where
  Left  x ~= Left  y = x ~= y
  Right x ~= Right y = x ~= y
  _       ~= _       = False
instance (NearEq a, NearEq b) => NearEq (a,b) where
  (x1,x2) ~= (y1,y2) = x1 ~= y1 && x2 ~= y2
instance (NearEq a, NearEq b, NearEq c) => NearEq (a,b,c) where
  (x1,x2,x3) ~= (y1,y2,y3) = x1 ~= y1 && x2 ~= y2 && x3 ~= y3