module Numeric.MixedTypes.Elementary
(
CanSqrt(..), CanSqrtSameType, CanSqrtCNSameType, specCanSqrtReal
, CanExp(..), CanExpSameType, specCanExpReal
, CanLog(..), CanLogSameType, CanLogCNSameType, specCanLogReal
, powUsingExpLog
, CanSinCos(..), CanSinCosSameType, specCanSinCosReal
, approxPi
)
where
import Numeric.MixedTypes.PreludeHiding
import qualified Prelude as P
import Text.Printf
import Test.Hspec
import Test.QuickCheck
import Numeric.CollectErrors
import Control.CollectErrors
import Numeric.MixedTypes.Literals
import Numeric.MixedTypes.Bool
import Numeric.MixedTypes.Eq
import Numeric.MixedTypes.Ord
import Numeric.MixedTypes.AddSub
import Numeric.MixedTypes.Ring
import Numeric.MixedTypes.Field
import Utils.Test.EnforceRange
class CanSqrt t where
type SqrtType t
type SqrtType t = t
sqrt :: t -> SqrtType t
default sqrt :: (SqrtType t ~ t, P.Floating t) => t -> SqrtType t
sqrt = P.sqrt
type CanSqrtSameType t = (CanSqrt t, SqrtType t ~ t)
type CanSqrtCNSameType t = (CanSqrt t, SqrtType t ~ EnsureCN t)
specCanSqrtReal ::
(Show t, Show (SqrtType t), Show (PowType (SqrtType t) Integer),
Arbitrary t,
CanTestCertainly (OrderCompareType (SqrtType t) Integer),
CanTestCertainly (EqCompareType (PowType (SqrtType t) Integer) t),
HasEqAsymmetric (PowType (SqrtType t) Integer) t,
HasOrderAsymmetric (SqrtType t) Integer, CanTestPosNeg t,
CanPow (SqrtType t) Integer, CanSqrt t)
=>
T t -> Spec
specCanSqrtReal (T typeName :: T t) =
describe (printf "CanSqrt %s" typeName) $ do
it "sqrt(x) >= 0" $ do
property $ \ (x :: t) ->
isCertainlyNonNegative x ==>
(sqrt x) ?>=?$ 0
it "sqrt(x)^2 = x" $ do
property $ \ (x :: t) ->
isCertainlyNonNegative x ==>
(sqrt x)^2 ?==?$ x
where
infix 4 ?==?$
(?==?$) :: (HasEqCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?==?$) = printArgsIfFails2 "?==?" (?==?)
infix 4 ?>=?$
(?>=?$) :: (HasOrderCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?>=?$) = printArgsIfFails2 "?>=?" (?>=?)
instance CanSqrt Double
instance
(CanSqrt a
, CanEnsureCE es a
, CanEnsureCE es (SqrtType a)
, SuitableForCE es)
=>
CanSqrt (CollectErrors es a)
where
type SqrtType (CollectErrors es a) = EnsureCE es (SqrtType a)
sqrt = lift1CE sqrt
class CanExp t where
type ExpType t
type ExpType t = t
exp :: t -> ExpType t
default exp :: (ExpType t ~ t, P.Floating t) => t -> ExpType t
exp = P.exp
type CanExpSameType t = (CanExp t, ExpType t ~ t)
specCanExpReal ::
(Show t, Show (ExpType t), Show (DivType Integer (ExpType t)),
Show (ExpType (AddType t t)),
Show (MulType (ExpType t) (ExpType t)),
Show (EnsureCN (ExpType t)), Arbitrary t,
CanEnsureCN (ExpType t),
CanTestCertainly (OrderCompareType Integer t),
CanTestCertainly (OrderCompareType t Integer),
CanTestCertainly (OrderCompareType (ExpType t) Integer),
CanTestCertainly
(EqCompareType
(EnsureCN (ExpType t)) (DivType Integer (ExpType t))),
CanTestCertainly
(EqCompareType
(ExpType (AddType t t)) (MulType (ExpType t) (ExpType t))),
CanNeg t,
HasEqAsymmetric
(ExpType (AddType t t)) (MulType (ExpType t) (ExpType t)),
HasEqAsymmetric
(EnsureCN (ExpType t)) (DivType Integer (ExpType t)),
HasOrderAsymmetric t Integer,
HasOrderAsymmetric (ExpType t) Integer,
HasOrderAsymmetric Integer t, CanAddAsymmetric t t,
CanMulAsymmetric (ExpType t) (ExpType t),
CanDiv Integer (ExpType t), CanExp t, CanExp (AddType t t),
NegType t ~ t,
CanEnforceRange t Integer) =>
T t -> Spec
specCanExpReal (T typeName :: T t) =
describe (printf "CanExp %s" typeName) $ do
it "exp(x) >= 0" $ do
property $ \ (x_ :: t) ->
let x = enforceRange (Just (-100000), Just 100000) x_ in
exp x ?>=?$ 0
it "exp(-x) == 1/(exp x)" $ do
property $ \ (x_ :: t) ->
let x = enforceRange (Just (-100000), Just 100000) x_ in
let ex = exp x in
(ex !>! 0) ==>
(ensureCN $ exp (-x)) ?==?$ 1/ex
it "exp(x+y) = exp(x)*exp(y)" $ do
property $ \ (x_ :: t) (y_ :: t) ->
let x = enforceRange (Just (-100000), Just 100000) x_ in
let y = enforceRange (Just (-100000), Just 100000) y_ in
(exp $ x + y) ?==?$ (exp x) * (exp y)
where
infix 4 ?==?$
(?==?$) :: (HasEqCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?==?$) = printArgsIfFails2 "?==?" (?==?)
infix 4 ?>=?$
(?>=?$) :: (HasOrderCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?>=?$) = printArgsIfFails2 "?>=?" (?>=?)
instance CanExp Double
instance
(CanExp a
, CanEnsureCE es a
, CanEnsureCE es (ExpType a)
, SuitableForCE es)
=>
CanExp (CollectErrors es a)
where
type ExpType (CollectErrors es a) = EnsureCE es (ExpType a)
exp = lift1CE exp
class CanLog t where
type LogType t
type LogType t = t
log :: t -> LogType t
default log :: (LogType t ~ t, P.Floating t) => t -> LogType t
log = P.log
type CanLogSameType t = (CanLog t, LogType t ~ t)
type CanLogCNSameType t = (CanLog t, LogType t ~ EnsureCN t)
specCanLogReal ::
(Show t, Show (LogType t), Show (LogType (DivType Integer t)),
Show (LogType (MulType t t)),
Show (AddType (LogType t) (LogType t)), Show (LogType (ExpType t)),
Arbitrary t, CanTestCertainly (OrderCompareType t Integer),
CanTestCertainly (OrderCompareType (DivType Integer t) Integer),
CanTestCertainly
(EqCompareType (LogType (DivType Integer t)) (LogType t)),
CanTestCertainly (OrderCompareType (MulType t t) Integer),
CanTestCertainly (OrderCompareType (ExpType t) Integer),
CanTestCertainly
(EqCompareType
(LogType (MulType t t)) (AddType (LogType t) (LogType t))),
CanTestCertainly (OrderCompareType Integer t),
CanTestCertainly (EqCompareType (LogType (ExpType t)) t),
CanNeg (LogType t),
HasEqAsymmetric (LogType (DivType Integer t)) (LogType t),
HasEqAsymmetric
(LogType (MulType t t)) (AddType (LogType t) (LogType t)),
HasEqAsymmetric (LogType (ExpType t)) t,
HasOrderAsymmetric t Integer,
HasOrderAsymmetric (DivType Integer t) Integer,
HasOrderAsymmetric (MulType t t) Integer,
HasOrderAsymmetric (ExpType t) Integer,
HasOrderAsymmetric Integer t,
CanAddAsymmetric (LogType t) (LogType t), CanMulAsymmetric t t,
CanDiv Integer t, CanExp t, CanLog t, CanLog (DivType Integer t),
CanLog (MulType t t), CanLog (ExpType t),
LogType t ~ NegType (LogType t),
CanEnforceRange t Integer) =>
T t -> Spec
specCanLogReal (T typeName :: T t) =
describe (printf "CanLog %s" typeName) $ do
it "log(1/x) == -(log x)" $ do
property $ \ (x_ :: t) ->
let x = enforceRange (Just 0, Nothing) x_ in
x !>! 0 && (1/x) !>! 0 ==>
log (1/x) ?==?$ -(log x)
it "log(x*y) = log(x)+log(y)" $ do
property $ \ (x_ :: t) (y_ :: t) ->
let x = enforceRange (Just 0, Nothing) x_ in
let y = enforceRange (Just 0, Nothing) y_ in
x !>! 0 && y !>! 0 && x*y !>! 0 ==>
(log $ x * y) ?==?$ (log x) + (log y)
it "log(exp x) == x" $ do
property $ \ (x_ :: t) ->
let x = enforceRange (Just (-1000), Just 10000) x_ in
let ex = exp x in
(ex !>! 0) ==>
log ex ?==?$ x
where
infix 4 ?==?$
(?==?$) :: (HasEqCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?==?$) = printArgsIfFails2 "?==?" (?==?)
instance CanLog Double
instance
(CanLog a
, CanEnsureCE es a
, CanEnsureCE es (LogType a)
, SuitableForCE es)
=>
CanLog (CollectErrors es a)
where
type LogType (CollectErrors es a) = EnsureCE es (LogType a)
log = lift1CE log
instance CanPow Double Double where
powNoCN = (P.**)
type PowType Double Double = Double
pow = (P.**)
instance CanPow Double Rational where
powNoCN b e = b ^! (double e)
type PowType Double Rational = Double
pow b e = b ^ (double e)
instance CanPow Rational Double where
type PowTypeNoCN Rational Double = Double
powNoCN b e = (double b) ^! e
type PowType Rational Double = Double
pow b e = (double b) ^ e
instance CanPow Integer Double where
type PowTypeNoCN Integer Double = Double
powNoCN b e = (double b) ^! e
type PowType Integer Double = Double
pow b e = (double b) ^ e
instance CanPow Int Double where
type PowTypeNoCN Int Double = Double
powNoCN b e = (double b) ^! e
type PowType Int Double = Double
pow b e = (double b) ^ e
powUsingExpLog ::
(CanTestPosNeg t,
CanEnsureCN t,
CanEnsureCN (EnsureCN t),
EnsureCN t ~ EnsureCN (EnsureCN t),
CanLogCNSameType t,
CanMulSameType t,
CanMulSameType (EnsureCN t),
CanExpSameType (EnsureCN t),
CanTestInteger t,
CanTestZero t,
CanRecipCNSameType t)
=>
t -> t -> t -> t -> EnsureCN t
powUsingExpLog zero one b e =
case certainlyIntegerGetIt e of
Just n ->
powUsingMulRecip one b n
Nothing
| isCertainlyZero b && isCertainlyPositive e -> cn zero
| isCertainlyNonNegative b -> exp ((log b) * (ensureCN e))
| isCertainlyNegative b && certainlyNotInteger e -> noValueNumErrorCertainECN (Just b) err
| otherwise -> noValueNumErrorPotentialECN (Just b) err
where
err = NumError "powUsingExpLog: illegal power a^b with negative a and non-integer b"
class CanSinCos t where
type SinCosType t
type SinCosType t = t
cos :: t -> SinCosType t
default cos :: (SinCosType t ~ t, P.Floating t) => t -> SinCosType t
cos = P.cos
sin :: t -> SinCosType t
default sin :: (SinCosType t ~ t, P.Floating t) => t -> SinCosType t
sin = P.sin
type CanSinCosSameType t = (CanSinCos t, SinCosType t ~ t)
specCanSinCosReal ::
(Show t, Show (SinCosType t),
Show
(AddType
(PowType (SinCosType t) Integer) (PowType (SinCosType t) Integer)),
Show (SinCosType (SubType t t)),
Show
(SubType
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t))),
Show
(AddType
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t))),
Show (DivType (SinCosType t) (SinCosType t)),
Show (EnsureCN t), Arbitrary t, CanEnsureCN t,
CanTestCertainly (OrderCompareType Integer (SinCosType t)),
CanTestCertainly (OrderCompareType (SinCosType t) Integer),
CanTestCertainly
(EqCompareType
(AddType
(PowType (SinCosType t) Integer)
(PowType (SinCosType t) Integer))
Integer),
CanTestCertainly
(EqCompareType
(SinCosType (SubType t t))
(SubType
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t)))),
CanTestCertainly
(EqCompareType
(SinCosType (SubType t t))
(AddType
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t)))),
CanTestCertainly (OrderCompareType t Integer),
CanTestCertainly (OrderCompareType t Rational),
CanTestCertainly (OrderCompareType (SinCosType t) t),
CanTestCertainly
(OrderCompareType
(EnsureCN t) (DivType (SinCosType t) (SinCosType t))),
HasEqAsymmetric
(AddType
(PowType (SinCosType t) Integer) (PowType (SinCosType t) Integer))
Integer,
HasEqAsymmetric
(SinCosType (SubType t t))
(SubType
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t))),
HasEqAsymmetric
(SinCosType (SubType t t))
(AddType
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t))),
HasOrderAsymmetric t Integer, HasOrderAsymmetric t Rational,
HasOrderAsymmetric (SinCosType t) t,
HasOrderAsymmetric (SinCosType t) Integer,
HasOrderAsymmetric
(EnsureCN t) (DivType (SinCosType t) (SinCosType t)),
HasOrderAsymmetric Integer (SinCosType t), CanSub t t,
CanSub
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t)),
CanAddAsymmetric
(PowType (SinCosType t) Integer) (PowType (SinCosType t) Integer),
CanAddAsymmetric
(MulType (SinCosType t) (SinCosType t))
(MulType (SinCosType t) (SinCosType t)),
CanPow (SinCosType t) Integer,
CanMulAsymmetric (SinCosType t) (SinCosType t),
CanDiv (SinCosType t) (SinCosType t), CanSinCos t,
CanSinCos (SubType t t))
=>
T t -> Spec
specCanSinCosReal (T typeName :: T t) =
describe (printf "CanSinCos %s" typeName) $ do
it "-1 <= sin(x) <= 1" $ do
property $ \ (x :: t) ->
(-1) ?<=?$ (sin x) .&&. (sin x) ?<=?$ 1
it "-1 <= cos(x) <= 1" $ do
property $ \ (x :: t) ->
(-1) ?<=?$ (cos x) .&&. (cos x) ?<=?$ 1
it "cos(x)^2 + sin(x)^2 = 1" $ do
property $ \ (x :: t) ->
(sin x)^2 + (cos x)^2 ?==?$ 1
it "sin(x-y) = sin(x)cos(y) - cos(x)sin(y)" $ do
property $ \ (x :: t) (y :: t) ->
(sin $ x - y) ?==?$ (sin x)*(cos y) - (cos x)*(sin y)
it "cos(x-y) = cos(x)cos(y) + sin(x)sin(y)" $ do
property $ \ (x :: t) (y :: t) ->
(cos $ x - y) ?==?$ (cos x)*(cos y) + (sin x)*(sin y)
it "sin(x) < x < tan(x) for x in [0,pi/2]" $ do
property $ \ (x :: t) ->
x !>=! 0 && x !<=! 1.57 && (cos x) !>! 0 ==>
(sin x) ?<=?$ x .&&. (ensureCN x) ?<=?$ (sin x)/(cos x)
where
infix 4 ?==?$
(?==?$) :: (HasEqCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?==?$) = printArgsIfFails2 "?==?" (?==?)
infix 4 ?<=?$
(?<=?$) :: (HasOrderCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?<=?$) = printArgsIfFails2 "?<=?" (?<=?)
instance CanSinCos Double
instance
(CanSinCos a
, CanEnsureCE es a
, CanEnsureCE es (SinCosType a)
, SuitableForCE es)
=>
CanSinCos (CollectErrors es a)
where
type SinCosType (CollectErrors es a) = EnsureCE es (SinCosType a)
sin = lift1CE sin
cos = lift1CE cos
approxPi :: (P.Floating t) => t
approxPi = P.pi