# language FlexibleInstances #
-----------------------------------------------------------------------------
-- |
  Copyright    :   ( C ) 2016 - 2017   , 2012 - 2015  
-- License     :  BSD3 (see LICENSE)
-- Maintainer  :  zocca.marco gmail
-- Stability   :  provisional
-- Portability :  portable
--
  Testing for values " near " zero
-----------------------------------------------------------------------------
module Numeric.Eps
  ( Epsilon(..), isNz, roundZero, roundOne, roundZeroOne,
    nearOne
  ) where
import Data.Complex
import Foreign.C.Types (CFloat, CDouble)

  | Provides a test to see if a quantity is near zero .
--
-- >>> nearZero (1e-11 :: Double)
-- False
--
  > > > nearZero ( 1e-17 : : Double )
-- True
--
  > > > nearZero ( 1e-5 : : Float )
-- False
--
  > > > nearZero ( 1e-7 : : Float )
-- True
class (Floating a, Num a) => Epsilon a where
  | Determine if a quantity is near zero .
  nearZero :: a -> Bool

-- | @'abs' a '<=' 1e-6@
instance Epsilon Float where
  nearZero a = abs a <= 1e-6

-- | @'abs' a '<=' 1e-12@
instance Epsilon Double where
  nearZero a = abs a <= 1e-12

-- | @'abs' a '<=' 1e-6@
instance Epsilon CFloat where
  nearZero a = abs a <= 1e-6

-- | @'abs' a '<=' 1e-12@
instance Epsilon CDouble where
  nearZero a = abs a <= 1e-12


-- | @'magnitude' a '<=' 1e-6@
instance Epsilon (Complex Float) where
  nearZero a = magnitude a <= 1e-6

-- | @'magnitude' a '<=' 1e-12@
instance Epsilon (Complex Double) where
  nearZero a = magnitude a <= 1e-12

-- | @'magnitude' a '<=' 1e-6@
instance Epsilon (Complex CFloat) where
  nearZero a = magnitude a <= 1e-6

-- | @'magnitude' a '<=' 1e-12@
instance Epsilon (Complex CDouble) where
  nearZero a = magnitude a <= 1e-12



-- * Rounding operations


  | Is this quantity close to 1 ?
nearOne :: Epsilon a => a -> Bool
nearOne x = nearZero (1 - x)

-- | Is this quantity distinguishable from 0 ?
isNz :: Epsilon a => a -> Bool
isNz x = not (nearZero x)

withDefault :: (t -> Bool) -> t -> t -> t
withDefault q d x | q x = d
                  | otherwise = x

roundZero, roundOne, roundZeroOne :: Epsilon a => a -> a
roundZero = withDefault nearZero (fromIntegral (0 :: Int))
roundOne = withDefault nearOne (fromIntegral (1 :: Int))

with2Defaults :: (t -> Bool) -> (t -> Bool) -> t -> t -> t -> t
with2Defaults q1 q2 d1 d2 x | q1 x = d1
                            | q2 x = d2
                            | otherwise = x

  | Round to respectively 0 or 1
roundZeroOne = with2Defaults nearZero nearOne (fromIntegral (0 :: Int)) (fromIntegral (1 :: Int))

---------------------------------------------------------------------------
 |
 License     :  BSD3 (see LICENSE)
 Maintainer  :  zocca.marco gmail
 Stability   :  provisional
 Portability :  portable

---------------------------------------------------------------------------

 >>> nearZero (1e-11 :: Double)
 False

 True

 False

 True
 | @'abs' a '<=' 1e-6@
 | @'abs' a '<=' 1e-12@
 | @'abs' a '<=' 1e-6@
 | @'abs' a '<=' 1e-12@
 | @'magnitude' a '<=' 1e-6@
 | @'magnitude' a '<=' 1e-12@
 | @'magnitude' a '<=' 1e-6@
 | @'magnitude' a '<=' 1e-12@
 * Rounding operations
 | Is this quantity distinguishable from 0 ?
# language FlexibleInstances #
  Copyright    :   ( C ) 2016 - 2017   , 2012 - 2015  
  Testing for values " near " zero
module Numeric.Eps
  ( Epsilon(..), isNz, roundZero, roundOne, roundZeroOne,
    nearOne
  ) where
import Data.Complex
import Foreign.C.Types (CFloat, CDouble)

  | Provides a test to see if a quantity is near zero .
  > > > nearZero ( 1e-17 : : Double )
  > > > nearZero ( 1e-5 : : Float )
  > > > nearZero ( 1e-7 : : Float )
class (Floating a, Num a) => Epsilon a where
  | Determine if a quantity is near zero .
  nearZero :: a -> Bool

instance Epsilon Float where
  nearZero a = abs a <= 1e-6

instance Epsilon Double where
  nearZero a = abs a <= 1e-12

instance Epsilon CFloat where
  nearZero a = abs a <= 1e-6

instance Epsilon CDouble where
  nearZero a = abs a <= 1e-12


instance Epsilon (Complex Float) where
  nearZero a = magnitude a <= 1e-6

instance Epsilon (Complex Double) where
  nearZero a = magnitude a <= 1e-12

instance Epsilon (Complex CFloat) where
  nearZero a = magnitude a <= 1e-6

instance Epsilon (Complex CDouble) where
  nearZero a = magnitude a <= 1e-12





  | Is this quantity close to 1 ?
nearOne :: Epsilon a => a -> Bool
nearOne x = nearZero (1 - x)

isNz :: Epsilon a => a -> Bool
isNz x = not (nearZero x)

withDefault :: (t -> Bool) -> t -> t -> t
withDefault q d x | q x = d
                  | otherwise = x

roundZero, roundOne, roundZeroOne :: Epsilon a => a -> a
roundZero = withDefault nearZero (fromIntegral (0 :: Int))
roundOne = withDefault nearOne (fromIntegral (1 :: Int))

with2Defaults :: (t -> Bool) -> (t -> Bool) -> t -> t -> t -> t
with2Defaults q1 q2 d1 d2 x | q1 x = d1
                            | q2 x = d2
                            | otherwise = x

  | Round to respectively 0 or 1
roundZeroOne = with2Defaults nearZero nearOne (fromIntegral (0 :: Int)) (fromIntegral (1 :: Int))
