content: -- Copyright 2013 Evan Laforge
  -- This program is distributed under the terms of the GNU General Public
  -- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt
  
  -- | ApproxEq class for comparing floating point numbers.
  module Util.Test.ApproxEq (ApproxEq(eq), neq, compare) where
  import Prelude hiding (compare)
  import qualified Data.Text as Text
  
  import qualified Util.Num as Num
  
  
  compare :: (ApproxEq a, Ord a) => Double -> a -> a -> Ordering
  compare eta a b
      | eq eta a b = EQ
      | a > b = GT
      | otherwise = LT
  
  class ApproxEq a where
      eq :: Double -> a -> a -> Bool
  
  neq :: ApproxEq a => Double -> a -> a -> Bool
  neq eta x y = not $ eq eta x y
  
  instance ApproxEq Float where
      eq eta x y = abs (x - y) <= Num.d2f eta
  instance ApproxEq Double where
      eq eta x y = abs (x - y) <= eta
  
  -- * prelude types
  
  instance ApproxEq Char where eq _ = (==)
  instance ApproxEq Int where eq _ = (==)
  instance ApproxEq Integer where eq _ = (==)
  instance ApproxEq Bool where eq _ = (==)
  
  instance ApproxEq a => ApproxEq [a] where
      eq eta = go
          where
          go [] [] = True
          go [] _ = False
          go _ [] = False
          go (x:xs) (y:ys) = eq eta x y && go xs ys
  
  instance ApproxEq a => ApproxEq (Maybe a) where
      eq eta (Just x) (Just y) = eq eta x y
      eq _ _ _ = False
  
  instance (ApproxEq a, ApproxEq b) => ApproxEq (Either a b) where
      eq eta (Right x) (Right y) = eq eta x y
      eq eta (Left x) (Left y) = eq eta x y
      eq _ _ _ = False
  
  instance (ApproxEq a, ApproxEq b) => ApproxEq (a, b) where
      eq eta (a, b) (a', b') = eq eta a a' && eq eta b b'
  instance (ApproxEq a, ApproxEq b, ApproxEq c) => ApproxEq (a, b, c) where
      eq eta (a, b, c) (a', b', c') = eq eta a a' && eq eta b b' && eq eta c c'
  instance (ApproxEq a, ApproxEq b, ApproxEq c, ApproxEq d) =>
          ApproxEq (a, b, c, d) where
      eq eta (a, b, c, d) (a', b', c', d') =
          eq eta a a' && eq eta b b' && eq eta c c' && eq eta d d'
  
  -- * hackage types
  
  instance ApproxEq Text.Text where eq _ = (==)
  