packages feed

numeric-prelude-0.3: test/Test/MathObj/PartialFraction.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
module Test.MathObj.PartialFraction where

import qualified MathObj.PartialFraction      as PartialFraction
import qualified MathObj.Polynomial           as Poly
import qualified Number.Ratio                 as Ratio

import qualified Algebra.PrincipalIdealDomain as PID
-- import qualified Algebra.Ring                 as Ring
import qualified Algebra.Indexable            as Indexable
import qualified Algebra.Vector               as Vector
-- import Algebra.Vector((*>))

import qualified Algebra.Laws as Laws
import qualified Test.QuickCheck as QC

import Control.Monad.HT as M
import Test.NumericPrelude.Utility (testUnit)
import Test.QuickCheck (quickCheck)
import qualified Test.HUnit as HUnit


import NumericPrelude.Base as P
import NumericPrelude.Numeric as NP


{- * Properties for generic types -}

fractionConv :: (PID.C a, Indexable.C a) => [a] -> a -> Bool
fractionConv xs y =
   PartialFraction.toFraction (PartialFraction.fromFactoredFraction xs y) ==
   y % product xs

fractionConvAlt :: (PID.C a, Indexable.C a) => [a] -> a -> Bool
fractionConvAlt xs y =
   PartialFraction.fromFactoredFraction xs y ==
   PartialFraction.fromFactoredFractionAlt xs y

scaleInt :: (PID.C a, Indexable.C a) => a -> PartialFraction.T a -> Bool
scaleInt k a =
   PartialFraction.toFraction (PartialFraction.scaleInt k a) ==
   Ratio.scale k (PartialFraction.toFraction a)

add :: (PID.C a, Indexable.C a) => PartialFraction.T a -> PartialFraction.T a -> Bool
add = Laws.homomorphism PartialFraction.toFraction (+) (+)

sub :: (PID.C a, Indexable.C a) => PartialFraction.T a -> PartialFraction.T a -> Bool
sub = Laws.homomorphism PartialFraction.toFraction (-) (-)

mul :: (PID.C a, Indexable.C a) => PartialFraction.T a -> PartialFraction.T a -> Bool
mul = Laws.homomorphism PartialFraction.toFraction (*) (*)



{- * Properties for Integers -}

{- |
Arbitrary instance of that type generates irreducible elements for tests.
Choosing from a list of examples is a simple yet effective design.
If we would construct irreducible elements by a clever algorithm
we might obtain multiple primes only rarely.
-}
newtype SmallPrime = SmallPrime {intFromSmallPrime :: Integer}

type IntFraction = ([SmallPrime],Integer)

instance QC.Arbitrary SmallPrime where
   arbitrary =
      let primes = [2,3,5,7,11,13]
      in  fmap SmallPrime $ QC.elements (primes ++ map negate primes)

instance Show SmallPrime where
   show = show . intFromSmallPrime


fractionConvInt :: [SmallPrime] -> Integer -> Bool
fractionConvInt =
   fractionConv . map intFromSmallPrime

fractionConvAltInt :: [SmallPrime] -> Integer -> Bool
fractionConvAltInt =
   fractionConvAlt . map intFromSmallPrime

fromSmallPrimes :: IntFraction -> PartialFraction.T Integer
fromSmallPrimes (xs,y) =
   PartialFraction.fromFactoredFraction (map intFromSmallPrime xs) y


scaleIntInt :: Integer -> IntFraction -> Bool
scaleIntInt k a =
   scaleInt k (fromSmallPrimes a)

addInt :: IntFraction -> IntFraction -> Bool
addInt q0 q1 =
   add
      (fromSmallPrimes q0)
      (fromSmallPrimes q1)

subInt :: IntFraction -> IntFraction -> Bool
subInt q0 q1 =
   sub
      (fromSmallPrimes q0)
      (fromSmallPrimes q1)

mulInt :: IntFraction -> IntFraction -> Bool
mulInt q0 q1 =
   mul
      (fromSmallPrimes q0)
      (fromSmallPrimes q1)


intTests :: HUnit.Test
intTests =
   HUnit.TestLabel "integer" $
   HUnit.TestList $
   map testUnit $
      ("conversion between partial and ordinary fraction", quickCheck fractionConvInt) :
      ("two conversion routines from factored fractions", quickCheck fractionConvAltInt) :
      ("integer scaling", quickCheck scaleIntInt) :
      ("addition", quickCheck addInt) :
      ("subtraction", quickCheck subInt) :
      ("multiplication", quickCheck mulInt) :
      []


{- * Properties for Polynomials -}

newtype IrredPoly = IrredPoly {polyFromIrredPoly :: Poly.T Rational}

type RatPolynomial = Poly.T Rational
type PolyFraction = ([IrredPoly],RatPolynomial)

instance QC.Arbitrary IrredPoly where
   arbitrary =
      do poly <- QC.elements (map Poly.fromCoeffs [[2,3],[2,0,1],[3,0,1],[1,-3,0,1]])
         unit <- M.until (not. isZero) QC.arbitrary
         return (IrredPoly (unit Vector.*> poly))

instance Show IrredPoly where
   show = show . polyFromIrredPoly


fractionConvPoly :: [IrredPoly] -> RatPolynomial -> Bool
fractionConvPoly =
   fractionConv . map polyFromIrredPoly

fractionConvAltPoly :: [IrredPoly] -> RatPolynomial -> Bool
fractionConvAltPoly =
   fractionConvAlt . map polyFromIrredPoly

fromIrredPolys :: PolyFraction -> PartialFraction.T RatPolynomial
fromIrredPolys (xs,y) =
   PartialFraction.fromFactoredFraction (map polyFromIrredPoly xs) y


scaleIntPoly :: RatPolynomial -> PolyFraction -> Bool
scaleIntPoly k a =
   scaleInt k (fromIrredPolys a)

addPoly :: PolyFraction -> PolyFraction -> Bool
addPoly q0 q1 =
   add
      (fromIrredPolys q0)
      (fromIrredPolys q1)

subPoly :: PolyFraction -> PolyFraction -> Bool
subPoly q0 q1 =
   sub
      (fromIrredPolys q0)
      (fromIrredPolys q1)

mulPoly :: PolyFraction -> PolyFraction -> Bool
mulPoly q0 q1 =
   mul
      (fromIrredPolys q0)
      (fromIrredPolys q1)



polyTests :: HUnit.Test
polyTests =
   HUnit.TestLabel "polynomial" $
   HUnit.TestList $
   map testUnit $
{- this test fails, because addition may result in leading zero coefficients,
      that is, polynomial addition does not contain a normalization
      if it would contain one, we would exclude computable reals -}
-- wrong     ("conversion between partial and ordinary fraction", quickCheck fractionConvPoly) :
-- wrong     ("two conversion routines from factored fractions", quickCheck fractionConvAltPoly) :
-- too slow      ("integer scaling", quickCheck scaleIntPoly) :
-- too slow      ("addition", quickCheck addPoly) :
-- too slow      ("subtraction", quickCheck subPoly) :
-- too slow      ("multiplication", quickCheck mulPoly) :
      []


tests :: HUnit.Test
tests =
   HUnit.TestLabel "partial fraction" $
   HUnit.TestList $
      intTests :
--      polyTests :
      []