hasquant-0.5.0.2: test/hspec/QuantLib/Spec/Helpers.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | Shared arbitrary instances and value-comparison helpers used by more than
-- one @QuantLib.Spec.*@ module. Split out of the former single-file
-- @MainTest.hs@ (see CLAUDE.md's "Documentation upkeep" for the module
-- layout this belongs to).
module QuantLib.Spec.Helpers (
ValidDay(..)
, InvalidDay(..)
, areClose
, closePrec
, listClose
, listCloseRel
, binomialsClose
) where
import Data.Time.Calendar
import Data.List(delete)
import Test.QuickCheck(elements, Arbitrary(arbitrary))
import QuantLib.Time.Date(minDate, maxDate)
import QuantLib.Time.Schedule(Frequency(..))
import qualified QuantLib.Settings as Settings
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 < Settings.epsilon * Settings.epsilon
|| diff <= Settings.epsilon * abs x1 && diff <= Settings.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)