mixed-types-num-0.1.0.0: src/Numeric/MixedTypes/Field.hs
{-|
Module : Numeric.MixedType.Field
Description : Bottom-up typed division
Copyright : (c) Michal Konecny
License : BSD3
Maintainer : mikkonecny@gmail.com
Stability : experimental
Portability : portable
-}
module Numeric.MixedTypes.Field
(
-- * Field
CanAddSubMulDivBy, Field, CertainlyEqField, OrderedField, OrderedCertainlyField
-- * Division
, CanDiv(..), CanDivBy, CanDivSameType, CanRecip, CanRecipSameType
, (/), recip
, powUsingMulRecip
-- ** Tests
, specCanDiv, specCanDivNotMixed, CanDivX
)
where
import Numeric.MixedTypes.PreludeHiding
import qualified Prelude as P
import Text.Printf
-- import qualified Data.List as List
import Test.Hspec
import Test.QuickCheck
import Numeric.MixedTypes.Literals
import Numeric.MixedTypes.Bool
import Numeric.MixedTypes.Eq
import Numeric.MixedTypes.Ord
-- import Numeric.MixedTypes.MinMaxAbs
-- import Numeric.MixedTypes.AddSub
import Numeric.MixedTypes.Ring
{----- Field -----}
type CanAddSubMulDivBy t s =
(CanAddSubMulBy t s, CanDivBy t s)
type Field t =
(Ring t, CanDivSameType t, CanRecipSameType t,
CanAddSubMulDivBy t Rational,
CanAddSubMulDivBy t Integer,
CanAddSubMulDivBy t Int
)
type CertainlyEqField t = (Field t, CertainlyEqRing t)
type OrderedField t =
(Field t, OrderedRing t, HasOrder t Rational)
type OrderedCertainlyField t =
(CertainlyEqField t, OrderedCertainlyRing t, HasOrderCertainly t Rational)
{---- Division -----}
{-|
A replacement for Prelude's binary `P./`. If @t1 = t2@ and @Fractional t1@,
then one can use the default implementation to mirror Prelude's @/@.
-}
class CanDiv t1 t2 where
type DivType t1 t2
type DivType t1 t2 = t1 -- default
divide :: t1 -> t2 -> DivType t1 t2
default divide :: (DivType t1 t2 ~ t1, t1~t2, P.Fractional t1) => t1 -> t1 -> t1
divide = (P./)
(/) :: (CanDiv t1 t2) => t1 -> t2 -> DivType t1 t2
(/) = divide
type CanRecip t =
(CanDiv Integer t)
type CanRecipSameType t =
(CanDiv Integer t, DivType Integer t ~ t)
recip :: (CanRecip t) => t -> DivType Integer t
recip = divide 1
type CanDivBy t1 t2 =
(CanDiv t1 t2, DivType t1 t2 ~ t1)
type CanDivSameType t =
CanDivBy t t
{-| Compound type constraint useful for test definition. -}
type CanDivX t1 t2 =
(CanDiv t1 t2,
Show t1, Arbitrary t1,
Show t2, Arbitrary t2,
Show (DivType t1 t2),
HasEqCertainly t1 (DivType t1 t2))
{-|
HSpec properties that each implementation of CanDiv should satisfy.
-}
specCanDiv ::
(CanRecip t1, CanRecip (DivType Integer t1),
Show (DivType Integer (DivType Integer t1)),
HasEqCertainly t1 (DivType Integer (DivType Integer t1)),
CanTestZero (DivType Integer t1),
CanDivX t1 t2,
CanTestZero t1,
CanTestZero t2,
CanDivX t1 t1,
CanMulX t1 (DivType t1 t2),
ConvertibleExactly Integer t2, ConvertibleExactly Integer t1)
=>
T t1 -> T t2 -> Spec
specCanDiv (T typeName1 :: T t1) (T typeName2 :: T t2) =
describe (printf "CanDiv %s %s" typeName1 typeName2) $ do
it "recip(recip x) = x" $ do
property $ \ (x :: t1) ->
(isNonZero x && isNonZero (recip x)) ==>
recip (recip x) ?==?$ x
it "x/1 = x" $ do
property $ \ (x :: t1) -> let one = (convertExactly 1 :: t2) in (x / one) ?==?$ x
it "x/x = 1" $ do
property $ \ (x :: t1) ->
(isNonZero x) ==>
let one = (convertExactly 1 :: t1) in (x / x) ?==?$ one
it "x/y = x*(1/y)" $ do
property $ \ (x :: t1) (y :: t2) ->
(isNonZero y) ==>
let one = (convertExactly 1 :: t1) in (x / y) ?==?$ x * (one/y)
where
infix 4 ?==?$
(?==?$) :: (HasEqCertainlyAsymmetric a b, Show a, Show b) => a -> b -> Property
(?==?$) = printArgsIfFails2 "?==?" (?==?)
{-|
HSpec properties that each implementation of CanDiv should satisfy.
-}
specCanDivNotMixed ::
(CanRecip t, CanRecip (DivType Integer t),
Show (DivType Integer (DivType Integer t)),
HasEqCertainly t (DivType Integer (DivType Integer t)),
CanTestZero (DivType Integer t),
CanDivX t t,
CanTestZero t,
CanMulX t (DivType t t),
ConvertibleExactly Integer t)
=>
T t -> Spec
specCanDivNotMixed t = specCanDiv t t
instance CanDiv Int Int where
type DivType Int Int = Rational
divide a b = (rational a) P./ (rational b)
instance CanDiv Integer Integer where
type DivType Integer Integer = Rational
divide a b = (rational a) P./ (rational b)
instance CanDiv Rational Rational
instance CanDiv Double Double
instance CanDiv Int Integer where
type DivType Int Integer = Rational
divide a b = (rational a) P./ (rational b)
instance CanDiv Integer Int where
type DivType Integer Int = Rational
divide a b = (rational a) P./ (rational b)
instance CanDiv Int Rational where
type DivType Int Rational = Rational
divide = convertFirst divide
instance CanDiv Rational Int where
type DivType Rational Int = Rational
divide = convertSecond divide
instance CanDiv Integer Rational where
type DivType Integer Rational = Rational
divide = convertFirst divide
instance CanDiv Rational Integer where
type DivType Rational Integer = Rational
divide = convertSecond divide
instance CanDiv Int Double where
type DivType Int Double = Double
divide = convertFirst divide
instance CanDiv Double Int where
type DivType Double Int = Double
divide = convertSecond divide
instance CanDiv Integer Double where
type DivType Integer Double = Double
divide = convertFirst divide
instance CanDiv Double Integer where
type DivType Double Integer = Double
divide = convertSecond divide
instance CanDiv Rational Double where
type DivType Rational Double = Double
divide = convertFirst divide
instance CanDiv Double Rational where
type DivType Double Rational = Double
divide = convertSecond divide
instance (CanDiv a b) => CanDiv [a] [b] where
type DivType [a] [b] = [DivType a b]
divide (x:xs) (y:ys) = (divide x y) : (divide xs ys)
divide _ _ = []
instance (CanDiv a b) => CanDiv (Maybe a) (Maybe b) where
type DivType (Maybe a) (Maybe b) = Maybe (DivType a b)
divide (Just x) (Just y) = Just (divide x y)
divide _ _ = Nothing
powUsingMulRecip ::
(CanBeInteger e,
CanRecipSameType t, CanMulSameType t, ConvertibleExactly Integer t)
=>
t -> e -> t
powUsingMulRecip x nPre
| n < 0 = recip $ powUsingMul x (negate n)
| otherwise = powUsingMul x n
where
n = integer nPre