--------------------------------------------------------- -- -- Module : HasloQCTestL -- Copyright : Bartosz Wójcik (2011) -- License : BSD3 -- -- Maintainer : bartek@sudety.it -- Stability : Unstable -- Portability : portable -- -- Part of haslo. Data types used for Quick Check. -- Properties set up. --------------------------------------------------------- {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} module Haslo.HasloQCTestL where import Haslo import Data.List import Data.Time import Test.QuickCheck import Text.PrettyShow caps = choose (1000::Amount,100000000) caps' b = choose (b::Amount,100000000) class Show a => ShowShort a where showShort :: a -> String data GUIClassic = CL1 Classical | CL2 Balloon | CL3 BalloonPlus | CL4 ReversBalloon | CL5 Bullet | CL6 UnfdBalloon | CL7 UnfdBalloonPlus deriving (Eq, Ord) instance Show GUIClassic where show (CL1 l) = show l show (CL2 l) = show l show (CL3 l) = show l show (CL4 l) = show l show (CL5 l) = show l show (CL6 l) = show l show (CL7 l) = show l instance PrettyShow GUIClassic where showWithLen n = showWithLen n . show instance ShowShort GUIClassic where showShort = filter (/= '(') . head . drop 1 . words . show instance Enum GUIClassic where toEnum 0 = CL1 $ Classical 0 0 0 0 toEnum 1 = CL2 $ Balloon 0 0 0 0 0 toEnum 2 = CL3 $ BalloonPlus 0 0 0 0 0 toEnum 3 = CL4 $ ReversBalloon 0 0 0 0 0 toEnum 4 = CL5 $ Bullet 0 0 0 0 toEnum 5 = CL6 $ UnfdBalloon 0 0 0 0 0 0 toEnum 6 = CL7 $ UnfdBalloonPlus 0 0 0 0 0 0 -- toEnum x = toEnum $ x `mod` 7 fromEnum (CL1 _) = 0 fromEnum (CL2 _) = 1 fromEnum (CL3 _) = 2 fromEnum (CL4 _) = 3 fromEnum (CL5 _) = 4 fromEnum (CL6 _) = 5 fromEnum (CL7 _) = 6 instance ClassicLoan GUIClassic where newLoanI (CL1 x) = newLoanI x newLoanI (CL2 x) = newLoanI x newLoanI (CL3 x) = newLoanI x newLoanI (CL4 x) = newLoanI x newLoanI (CL5 x) = newLoanI x newLoanI (CL6 x) = newLoanI x newLoanI (CL7 x) = newLoanI x extract (CL1 x) = extract x extract (CL2 x) = extract x extract (CL3 x) = extract x extract (CL4 x) = extract x extract (CL5 x) = extract x extract (CL6 x) = extract x extract (CL7 x) = extract x instance Balloons GUIClassic where balloon (CL2 x) = balloon x balloon (CL3 x) = balloon x balloon (CL5 x) = balloon x balloon (CL6 x) = balloon x balloon (CL7 x) = balloon x balloon l = error $ show l ++ " is not a balloon" instance UnfdBalloons GUIClassic where eXtendedDuration (CL6 x) = eXtendedDuration x eXtendedDuration (CL7 x) = eXtendedDuration x eXtendedDuration l = error $ show l ++ " is not a UnfdBalloon" {- data GUIFee = Fee1 FeeFinanced | Fee2 FeeAsLateInterest | Fee3 FeeLimitingFstInstalment deriving (Eq, Ord, Show) instance Enum GUIFee where toEnum 0 = Fee1 FeeFinanced toEnum 1 = Fee2 FeeAsLateInterest toEnum 2 = Fee3 FeeLimitingFstInstalment fromEnum (Fee1 _) = 0 fromEnum (Fee2 _) = 1 fromEnum (Fee3 _) = 2 instance FeeClass GUIFee where addFee (Fee1 _) = addFee FeeFinanced addFee (Fee2 _) = addFee FeeAsLateInterest addFee (Fee3 _) = addFee FeeLimitingFstInstalment instance ShowShort GUIFee where showShort = head . drop 1 . words . show feeList = take 3 $ map showShort [Fee1 FeeFinanced ..] -} -- | Poperties of instalment loan for given input parameters. instance Arbitrary GUIClassic where arbitrary = do p <- frequency [--(15,return paramDT), (70,return paramMT), (30,return paramYT)] -- Duration limited to 30 years n <- choose (3::Duration, 30 * (freqPerYear $ freq p)) -- Deferment limited to 12 instalments d <- choose (0::Duration,12) -- Interest rate limited to 20%. Selected like for humans: max 4 digits after decimal point. r <- frequency [(1,return 0) ,(99,liftM ((/10^6) . fromIntegral) $ choose (0::Int,2*10^5)) ] -- Balloon amount and capital limited to 10^8 (in € this makes 10^6) b <- caps c <- caps cBal <- caps' b -- Instalment for reversal balloon limited to 10^6 (in € this makes 10^4) i <- choose (0::Amount,100000) -- Capital of reversal balloon selected so, that balloon amount will be positive cRevBal <- choose (calcCapCl (fromIntegral i) n (cE2N (freq p) r) d ,i * fromIntegral n * 2 + 1000000) -- Max duration of extended period of UnfdBalloons limited so that total duration doesn't extend 30 years x <- choose (1::Duration, 30 * (freqPerYear $ freq p) - n + 1) oneof [return $ CL1 $ Classical c n d r ,return $ CL2 $ Balloon cBal n d r b ,return $ CL3 $ BalloonPlus cBal n d r b ,return $ CL4 $ ReversBalloon cRevBal n d r i ,return $ CL5 $ Bullet c n 0 r ,return $ CL6 $ UnfdBalloon cBal n d r b x ,return $ CL7 $ UnfdBalloonPlus cBal n d r b x ] data TestL = TestL (GUIClassic,InstalmentPlanParam) instance Show TestL where show (TestL (loan, IPP fr rd)) = show loan ++ " " ++ showWithLen 8 fr ++ showWithLen 9 rd instance Arbitrary TestL where arbitrary = do l <- arbitrary p <- if duration (extract l) <= 30 then frequency [--(15,return paramDT), (10,return paramMR), (40,return paramYR), (10,return paramMT), (40,return paramYT)] else oneof [return paramMT ,return paramMR ] return $ TestL (l, p) instance Testable (ValidMonad Bool) where property (Right b) = property b property (Left err) = property False instance Testable (ValidMonad ()) where property (Right _) = property True property (Left err) = error $ show err testLoan :: TestL -> ValidMonad InstalmentPlan testLoan (TestL (loan, ipp)) = runWithIPP ipp $ newLoanI loan propInstPlan :: TestL -> ValidMonad Bool propInstPlan p@(TestL (loan, ipp)) = testLoan p >>= \ip -> return (instalmentPlanCheck (principal $ extract loan) (fstInst p) ip) --propInstPlan :: TestL -> ValidMonad Bool --propInstPlan p@(TestL (_,_,amt,_,_,_)) = testLoan p >>= \ip -> -- return (instalmentPlanCheck amt (fstInst p) ip) --propInstPlanM :: TestL -> ValidMonad () --propInstPlanM p@(TestL (_,_,amt,_,_,_)) = testLoan p >>= -- instalmentPlanCheckM amt (fstInst p) fstInst :: TestL -> Double fstInst (TestL (CL5 (Bullet amt dur def rat), ipp)) = fromIntegral $ myRound (rounding ipp) $ rawCalcMaxFstInst (fromIntegral amt) dur (cE2N (freq ipp) rat) def fstInst _ = 0 instance Arbitrary Freq where arbitrary = oneof [return Daily, return Monthly, return Yearly] -- | Properity of interest rate conversion. -- Interest rate has to be positive. -- Daily interest rate not greater than 100%. prop_cEN :: (Freq,Double) -> Property prop_cEN (f,x) = x >= 0 && (x <= 1 || f /= Daily) ==> abs (cE2N f (cN2E f x) - x) <= x * 1e-12 -- | Interest rate property doesn't work for Bullet and UnfdBalloon loans propRate :: TestL -> ValidMonad Bool propRate p@(TestL (loan, ipp@(IPP _ Truncated))) | fromEnum loan `elem` [0,1,2,3] = testLoan p >>= rateCheckTruncated (principal $ extract loan) (fstInst p) r | otherwise = return True where r = cE2N (freq ipp) (rate $ extract loan) propRate p@(TestL (loan, ipp@(IPP _ Rounded))) | fromEnum loan `elem` [0,1,2,3] = testLoan p >>= rateCheckRounded (principal $ extract loan) (fstInst p) r | otherwise = return True where r = cE2N (freq ipp) (rate $ extract loan)