hasquant-0.8.0.0: test/hspec/QuantLib/Spec/Helpers.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |Shared version checks, arbitrary instances and value-comparison helpers for @QuantLib.Spec.*@.
module QuantLib.Spec.Helpers (
ValidDay(..)
, InvalidDay(..)
, areClose
, closePrec
, listClose
, listCloseRel
, binomialsClose
, quantLibAtMost143
, unsupportedQuantLib144
) where
import Data.Time.Calendar
import Data.Char(isDigit)
import Data.List(delete)
import Text.Read(readMaybe)
import Test.QuickCheck(elements, Arbitrary(arbitrary))
import QuantLib.Time.Date(minDate, maxDate)
import QuantLib.Time.Schedule(Frequency(..))
import qualified QuantLib.Context as Context
quantLibAtMost143 :: Bool
quantLibAtMost143 = case break (== '.') Context.version of
(major, '.':rest) -> case (readMaybe major, readMaybe (takeWhile isDigit rest)) of
(Just major', Just minor') -> (major', minor') <= (1 :: Int, 43 :: Int)
_ -> False
_ -> False
unsupportedQuantLib144 :: String -> Context.Error -> Bool
unsupportedQuantLib144 name (Context.UnsupportedQuantLibVersion call required linked) =
call == name && required == "1.44" && linked == Context.version
unsupportedQuantLib144 _ _ = False
instance Arbitrary Frequency where
arbitrary = elements $ OtherFrequency `delete` [minBound .. ]
newtype ValidDay = ValidDay {validDay::Day} deriving (Show, Eq)
newtype InvalidDay = InvalidDay Day deriving (Show, Eq)
instance Arbitrary ValidDay where
arbitrary = do
d <- elements [toModifiedJulianDay minDate .. toModifiedJulianDay maxDate]
return $ ValidDay (ModifiedJulianDay d)
instance Arbitrary InvalidDay where
arbitrary = do
d <- elements $ [minD-500 .. minD-1] ++ [maxD+1 .. maxD+500]
return $ InvalidDay (ModifiedJulianDay d)
where minD = toModifiedJulianDay minDate
maxD = toModifiedJulianDay maxDate
-- literal translation of close from ql/math/comparison.hpp
areClose :: Double -> Double -> Bool
areClose x1 x2 = x1 == x2
|| x1 * x2 == 0 && diff < Context.epsilon * Context.epsilon
|| diff <= Context.epsilon * abs x1 && diff <= Context.epsilon * abs x2
where diff = abs(x1 - x2)
closePrec :: Double -> Double -> Double -> Bool
closePrec r p x = abs (x - r) < p
listClose :: (a -> Double) -> [Double] -> Double -> [a] -> Bool
listClose f x1 e x2 = (length x1 == length x2) && all (\(x, y) -> abs(x - f y) < e) (zip x1 x2)
-- |Like 'listClose', but the tolerance is relative to each expected value
-- rather than a fixed absolute epsilon -- for tables whose entries span more
-- than an order of magnitude (e.g. CVA corrections from 0.24bp to 32bp).
listCloseRel :: (a -> Double) -> [Double] -> Double -> [a] -> Bool
listCloseRel f x1 e x2 = (length x1 == length x2) && all (\(x, y) -> abs(x - f y) < e * abs x) (zip x1 x2)
-- |row-wise 'listClose' at 1.0e-6, for tables of per-engine results (e.g. the
-- binomial-tree grid in the equity option example)
binomialsClose :: [[Double]] -> [[Double]] -> Bool
binomialsClose expected actual =
length expected == length actual
&& and (zipWith (\e a -> listClose id e 1.0e-6 a) expected actual)