{-# 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 : []