never executed always true always false
    1 module QuantLib.Internal
    2   (
    3     Day -- reexport for simplicity
    4   , minDate
    5   , maxDate
    6 
    7   -- marshalling helpers
    8   , prePtr
    9   , preErrorCheck
   10   , errorCheck
   11 
   12   , fromMaybeBool
   13   , toMaybeBool
   14   , peekDynString
   15   , peekEnum
   16   , peekDouble
   17   , preEnum
   18   , preNum
   19   , preArray
   20   , withEnumArray
   21   , withIntArray
   22   , withBoolArray
   23   , withDoubleArray
   24   , withNonEmptyDoubleArray
   25   , withDoubleArrayRaw
   26   , withBoolArrayRaw
   27   , withDayPtr
   28   , withStringArray
   29   , peekCStringArray
   30   , fromEnumQuantity
   31   , toEnumQuantity
   32   , fromEnumDouble
   33   , toEnumDouble
   34   , fromEnumC
   35   , toEnumC
   36 
   37   , withDay
   38   , toDay
   39   , withMaybeDay
   40   , fromMaybeInt
   41   , toMaybeDay
   42   , peekDayArray
   43   , peekBoolArray
   44   , withDayArray
   45   , peekDoubleArray
   46   , peekDoubleVector
   47 
   48   , toSerial
   49   , fromSerial
   50   , qlSavedSettings
   51   , qlFreeSavedSettings
   52   , fromMaybeDouble
   53   , fromMaybeEnum
   54   , fromMaybeEnumQuantity
   55   , peekIntArray
   56   , peekIntArray'
   57   , peekUIntArray
   58   , peekWord
   59   , peekStructArray
   60   , peekPtrArray
   61   , Matrix(..)
   62   , realMatrix
   63   , objectMatrix
   64   , qlNullInteger
   65   , qlFreeAdditionalResults
   66 
   67   , uncurryNested
   68   , zipWith6
   69   )
   70 where
   71 
   72 import Foreign.C.Types(CUInt(..), CInt(..), CDouble(..))
   73 import Foreign.C.String(CString, peekCString, withCString)
   74 import Foreign.Ptr(Ptr, nullPtr, castPtr)
   75 import Foreign.ForeignPtr(FinalizerPtr, newForeignPtr)
   76 import Foreign.Marshal.Array(peekArray, withArray)
   77 import Foreign.Marshal.Utils(with, toBool, fromBool, withMany)
   78 import Foreign.Storable(peek, Storable)
   79 import Foreign.Marshal.Alloc(alloca)
   80 
   81 import Control.Exception(throwIO)
   82 import Control.Monad(when)
   83 import Data.Time.Calendar(Day(ModifiedJulianDay), toModifiedJulianDay, fromGregorian)
   84 import Data.List.NonEmpty(NonEmpty, toList)
   85 
   86 import Data.Vector.Storable(Vector, unsafeFromForeignPtr0)
   87 
   88 import QuantLib.Type(Error(DateConversion, CPlusPlusException))
   89 
   90 errorCheck :: Ptr CString -> IO ()
   91 errorCheck p = do
   92   a <- peek p
   93   when
   94     (a /= nullPtr)
   95     ((peekCString a <* qlFreeString a) >>= throwIO . CPlusPlusException)
   96 
   97 -- like alloca but initializes the allocated pointer with zero
   98 preErrorCheck :: (Ptr (Ptr a) -> IO b) -> IO b
   99 preErrorCheck = with nullPtr
  100 
  101 fromMaybeBool :: Maybe Bool -> CInt
  102 fromMaybeBool = maybe (-1) fromBool
  103 
  104 foreign import ccall safe "ql.h qlNullInteger" qlNullInteger :: CInt
  105 foreign import ccall safe "ql.h qlNullReal" qlNullReal :: CDouble
  106 
  107 fromMaybeInt :: (Integral a, Integral b) => Maybe a -> b
  108 fromMaybeInt = maybe (fromIntegral qlNullInteger) fromIntegral
  109 
  110 fromMaybeDouble :: Maybe Double -> CDouble
  111 fromMaybeDouble = maybe qlNullReal realToFrac
  112 
  113 -- |Marshals a Maybe of a plain by-value C++ enum whose lowest member maps to 0
  114 -- (true of every by-value enum bound so far) as a C int, using -1 as the
  115 -- ext::nullopt sentinel -- mirrors fromMaybeBool's convention.
  116 fromMaybeEnum :: Enum a => Maybe a -> CInt
  117 fromMaybeEnum = maybe (-1) (fromIntegral . fromEnum)
  118 
  119 -- |'Nothing' emits the enum's -1 sentinel (same convention as 'fromMaybeEnum'), paired with a
  120 -- placeholder quantity of 0 -- for enums like 'TimeUnit' that start at 0 and can't self-sentinel.
  121 fromMaybeEnumQuantity :: Enum a => Maybe (Word, a) -> (CInt, CInt)
  122 fromMaybeEnumQuantity = maybe (0, -1) fromEnumQuantity
  123 
  124 toMaybeBool :: CInt -> Maybe Bool
  125 toMaybeBool x = if x == -1 then Nothing else Just $ toBool x
  126 
  127 peekDynString :: CString -> IO String
  128 peekDynString x = peekCString x <* qlFreeString x
  129 
  130 peekEnum :: (Enum a) => Ptr CInt -> IO a
  131 peekEnum x = toEnum . fromIntegral <$> peek x
  132 
  133 peekDouble :: Ptr CDouble -> IO Double
  134 peekDouble x = realToFrac <$> peek x
  135 
  136 peekWord :: Ptr CUInt -> IO Word
  137 peekWord x = fromIntegral <$> peek x
  138 
  139 -- initialize pointer to a enum with a valid value before passing it to the function
  140 preEnum :: (Storable a, Bounded a) => (Ptr a -> IO b) -> IO b
  141 preEnum = with minBound
  142 
  143 preNum :: (Storable a, Num a) => (Ptr a -> IO b) -> IO b
  144 preNum = with 0
  145 
  146 foreign import ccall safe "ql.h qlFreeString" qlFreeString :: CString -> IO ()
  147 foreign import ccall safe "ql.h qlFreeInts" qlFreeInts :: Ptr CInt -> IO ()
  148 foreign import ccall safe "ql.h qlFreeUInts" qlFreeUInts :: Ptr CUInt -> IO ()
  149 foreign import ccall safe "ql.h qlFreeDoubles" qlFreeDoubles :: Ptr CDouble -> IO ()
  150 foreign import ccall safe "ql.h &qlFreeDoubles" qlFreeDoublesFin :: FinalizerPtr CDouble
  151 foreign import ccall safe "ql.h qlFreePointerArray" qlFreePointerArray :: Ptr (Ptr ()) -> IO ()
  152 foreign import ccall safe "ql.h qlFreeAdditionalResults" qlFreeAdditionalResults :: CUInt -> Ptr () -> IO ()
  153 foreign import ccall safe "ql.h qlFreeStringArray" qlFreeStringArray :: CUInt -> Ptr CString -> IO ()
  154 foreign import ccall safe "ql.h qlSavedSettings" qlSavedSettings :: IO (Ptr ())
  155 foreign import ccall safe "ql.h qlFreeSavedSettings" qlFreeSavedSettings :: Ptr () -> IO ()
  156 
  157 withLArray :: (Storable b) => (a -> b) -> [a] -> ((CUInt, Ptr b) -> IO c) -> IO c
  158 withLArray c x f = withArray (map c x) (\px -> f (fromIntegral $ length x, px))
  159 
  160 withEnumArray :: (Enum a) => [a] -> ((CUInt, Ptr CInt) -> IO b) -> IO b
  161 withEnumArray = withLArray (fromIntegral . fromEnum)
  162 
  163 withIntArray :: (Integral a, Num n, Storable n) => [a] -> ((CUInt, Ptr n) -> IO b) -> IO b
  164 withIntArray = withLArray fromIntegral
  165 
  166 withBoolArray :: [Bool] -> ((CUInt, Ptr CInt) -> IO b) -> IO b
  167 withBoolArray = withLArray fromBool
  168 
  169 withDoubleArray :: [Double] -> ((CUInt, Ptr CDouble) -> IO b) -> IO b
  170 withDoubleArray = withLArray realToFrac
  171 
  172 withNonEmptyDoubleArray :: NonEmpty Double -> ((CUInt, Ptr CDouble) -> IO b) -> IO b
  173 withNonEmptyDoubleArray x = withLArray realToFrac (toList x)
  174 
  175 withDoubleArrayRaw :: [Double] -> (Ptr CDouble -> IO b) -> IO b
  176 withDoubleArrayRaw x = withArray (map realToFrac x)
  177 
  178 withBoolArrayRaw :: [Bool] -> (Ptr CInt -> IO b) -> IO b
  179 withBoolArrayRaw x = withArray (map fromBool x)
  180 
  181 withDayArray :: [Day] -> ((CUInt, Ptr CInt) -> IO b) -> IO b
  182 withDayArray x f = mapM toSerial x >>= (`withArray` (\px -> f (fromIntegral $ length x, px)))
  183 
  184 withDayPtr :: [Day] -> (Ptr CInt -> IO a) -> IO a
  185 withDayPtr x f = mapM toSerial x >>= (`withArray` f)
  186 
  187 -- |An array of plain C strings, for a function taking a @std::vector<std::string>@-shaped
  188 -- argument as a flat @(count, char**)@ pair (the input-side counterpart of 'peekCStringArray'
  189 -- below) -- first needed for @SecondaryCosts@' string keys (@QuantLib.Instrument.Energy@).
  190 withStringArray :: [String] -> ((CUInt, Ptr CString) -> IO b) -> IO b
  191 withStringArray xs f = withMany withCString xs (\ps -> withArray ps (\p -> f (fromIntegral (length xs), p)))
  192 
  193 prePtr :: (Storable a) => (Ptr a -> IO b) -> IO b
  194 prePtr = alloca
  195 
  196 preArray :: ((Ptr CUInt, Ptr (Ptr a)) -> IO b) -> IO b
  197 preArray f = with 0 $
  198   \x -> with nullPtr $
  199     \y -> f (x, y)
  200 
  201 peekIntArray' :: (CInt -> b) -> Ptr CUInt -> Ptr (Ptr CInt) -> IO [b]
  202 peekIntArray' f pl pp = do
  203   l <- peek pl
  204   p <- peek pp
  205   map f <$> peekArray (fromIntegral l) p <* qlFreeInts p
  206 
  207 peekUIntArray :: Ptr CUInt -> Ptr (Ptr CUInt) -> IO [Word]
  208 peekUIntArray pl pp = do
  209   l <- peek pl
  210   p <- peek pp
  211   map fromIntegral <$> peekArray (fromIntegral l) p <* qlFreeUInts p
  212 
  213 peekIntArray :: Ptr CUInt -> Ptr (Ptr CInt) -> IO [Int]
  214 peekIntArray = peekIntArray' fromIntegral
  215 
  216 -- |An array of freshly heap-allocated (@DUP@'d) C strings -- the output-side counterpart of
  217 -- 'withStringArray'. Each element is read via 'peekCString' and the whole array (including every
  218 -- individual string) is then released via 'qlFreeStringArray' in one call, mirroring
  219 -- 'peekDoubleArray'\/'peekIntArray'\''s read-then-free shape.
  220 peekCStringArray :: Ptr CUInt -> Ptr (Ptr CString) -> IO [String]
  221 peekCStringArray pl pp = do
  222   l <- peek pl
  223   p <- peek pp
  224   raws <- peekArray (fromIntegral l) p
  225   strs <- mapM peekCString raws
  226   strs <$ qlFreeStringArray l p
  227 
  228 peekBoolArray :: Ptr CUInt -> Ptr (Ptr CInt) -> IO [Bool]
  229 peekBoolArray = peekIntArray' toBool
  230 
  231 peekDayArray :: Ptr CUInt -> Ptr (Ptr CInt) -> IO [Day]
  232 peekDayArray = peekIntArray' fromSerial
  233 
  234 peekDoubleArray :: Ptr CUInt -> Ptr (Ptr CDouble) -> IO [Double]
  235 peekDoubleArray pl pp = do
  236   l <- peek pl
  237   p <- peek pp
  238   map realToFrac <$> peekArray (fromIntegral l) p <* qlFreeDoubles p
  239 
  240 peekDoubleVector :: Ptr CUInt -> Ptr (Ptr CDouble) -> IO (Vector CDouble)
  241 peekDoubleVector pl pp = unsafeFromForeignPtr0 <$> (peek pp >>= newForeignPtr qlFreeDoublesFin) <*> (fromIntegral <$> peek pl)
  242 
  243 -- |Like 'peekIntArray'\''/'peekDoubleArray', but for an array of a C struct rather than a C
  244 -- primitive: reads the length and pointer out-params, walks the array via 'peekArray' (so the
  245 -- element type just needs a 'Storable' instance -- e.g. one built from c2hs @{#get#}@/@{#sizeof#}@
  246 -- hooks), converts every element via @convert@, /then/ hands the whole array to @freeFn@ to
  247 -- release. The conversion must run before the free -- unlike 'peekIntArray'\''/'peekDoubleArray',
  248 -- whose elements are self-contained primitives, a struct element read here may itself own
  249 -- further heap buffers (e.g. a @char*@/@double*@ field) that @convert@ still needs to dereference
  250 -- (via 'peekCString'/'peekArray' etc.); freeing first would leave it reading already-freed
  251 -- memory. @freeFn@ takes the element count because some frees need it (e.g.
  252 -- 'qlFreeAdditionalResults'); one that doesn't can ignore it.
  253 peekStructArray :: Storable a => (a -> IO b) -> (CUInt -> Ptr a -> IO ()) -> Ptr CUInt -> Ptr (Ptr a) -> IO [b]
  254 peekStructArray convert freeFn pl pp = do
  255   l <- peek pl
  256   p <- peek pp
  257   raws <- peekArray (fromIntegral l) p
  258   results <- mapM convert raws
  259   results <$ freeFn l p
  260 
  261 -- |Like 'peekStructArray' but for a @T**@ array of C++-owned pointers to live objects: peeks the
  262 -- length/pointer out-params, converts each raw pointer to a live Haskell value via @peekOne@
  263 -- (installing that value's own finalizer over the pointee), then frees only the array spine via
  264 -- 'qlFreePointerArray' -- not the pointees, whose lifetime @peekOne@ has now taken over.
  265 peekPtrArray :: (Ptr a -> IO b) -> Ptr CUInt -> Ptr (Ptr (Ptr a)) -> IO [b]
  266 peekPtrArray peekOne pl pp = do
  267   l <- peek pl
  268   p <- peek pp
  269   ptrs <- peekArray (fromIntegral l) p
  270   xs <- mapM peekOne ptrs
  271   xs <$ qlFreePointerArray (castPtr p)
  272 
  273 fromEnumQuantity :: (Enum a, Integral b, Integral c) => (b, a) -> (CInt, c)
  274 fromEnumQuantity (x, u) = (fromIntegral x, fromIntegral $ fromEnum u)
  275 
  276 toEnumQuantity :: (Enum a, Integral b, Integral c) => (CInt, c) -> (b, a)
  277 toEnumQuantity (x, u) = (fromIntegral x, toEnum $ fromIntegral u)
  278 
  279 fromEnumDouble :: (Enum a, Integral c) => (Double, a) -> (CDouble, c)
  280 fromEnumDouble (x, u) = (realToFrac x, fromIntegral $ fromEnum u)
  281 
  282 toEnumDouble :: (Enum a, Integral c) => (CDouble, c) -> (Double, a)
  283 toEnumDouble (x, u) = (realToFrac x, toEnum $ fromIntegral u)
  284 
  285 foreign import ccall safe "ql.h qlMinYear" qlMinYear :: CInt
  286 foreign import ccall safe "ql.h qlMinMonth" qlMinMonth :: CInt
  287 foreign import ccall safe "ql.h qlMinDay" qlMinDay :: CInt
  288 foreign import ccall safe "ql.h qlMinDateSerialNumber" qlMinDateSerialNumber :: CInt
  289 foreign import ccall safe "ql.h qlMaxDateSerialNumber" qlMaxDateSerialNumber :: CInt
  290 
  291 -- |Julian day of the QuantLib zero date
  292 qlStart :: CInt
  293 qlStart = minDateJulianDays - qlMinDateSerialNumber
  294   where minDateJulianDays = toModifiedJulianDay' $ fromGregorian (fromIntegral qlMinYear) (fromIntegral qlMinMonth) (fromIntegral qlMinDay)
  295 
  296 toModifiedJulianDay' :: Day -> CInt
  297 toModifiedJulianDay' = fromIntegral . toModifiedJulianDay
  298 
  299 fromSerial :: CInt -> Day
  300 fromSerial x = ModifiedJulianDay $ fromIntegral (x + qlStart)
  301 
  302 dayIsValid :: Day -> Bool
  303 dayIsValid x = s >= qlMinDateSerialNumber && s <= qlMaxDateSerialNumber
  304   where s = toModifiedJulianDay' x - qlStart
  305 
  306 toSerial :: Day -> IO CInt
  307 toSerial x | dayIsValid x = return $ toModifiedJulianDay' x - qlStart
  308            | otherwise = throwIO $ DateConversion x
  309 
  310 withDay :: Day -> (CInt -> IO a) -> IO a
  311 withDay x f = toSerial x >>= f
  312 
  313 toDay :: CInt -> Day
  314 toDay = fromSerial
  315 
  316 withMaybeDay :: Maybe Day -> (CInt -> IO a) -> IO a
  317 withMaybeDay x f = maybe (f 0) (`withDay` f) x
  318 
  319 -- |Unlike the -1 sentinel used by 'fromMaybeBool'/'fromMaybeEnum'/'toMaybeBool', the
  320 -- absent-date sentinel is 0: QuantLib serial 0 is not a representable date (serials
  321 -- start at 'qlMinDateSerialNumber'), so it is free to mean "no date". This matches
  322 -- 'withMaybeDay', which passes 0 in the other direction.
  323 toMaybeDay :: CInt -> Maybe Day
  324 toMaybeDay 0 = Nothing
  325 toMaybeDay x = Just $ fromSerial x
  326 
  327 -- |earliest allowed date in QuantLib
  328 minDate :: Day
  329 minDate = fromSerial qlMinDateSerialNumber
  330 
  331 -- |latest date allowed in QuantLib
  332 maxDate :: Day
  333 maxDate = fromSerial qlMaxDateSerialNumber
  334 
  335 data Matrix a = Matrix {matrixRows::Word, matrixColumns::Word, matrixData::[a]}
  336   deriving (Eq, Show)
  337 
  338 -- |'objectMatrix' specialised to 'Double'. Kept as a separate name for callers that
  339 -- need the element type pinned; the check and construction are identical.
  340 realMatrix :: Word -> Word -> [Double] -> Either String (Matrix Double)
  341 realMatrix = objectMatrix
  342 
  343 objectMatrix :: Word -> Word -> [a] -> Either String (Matrix a)
  344 objectMatrix rows cols d
  345   | rows * cols == fromIntegral (length d) = Right $ Matrix rows cols d
  346   | otherwise = Left $ "Data length " ++ show (length d)
  347       ++ " does not match dimensions " ++ show rows ++ "x" ++ show cols
  348 
  349 -- just a generic implementation to help when it's difficult to have Enum declaration due to complex module deps
  350 fromEnumC :: (Enum a, Integral b) => a -> b
  351 fromEnumC = fromIntegral . fromEnum
  352 
  353 -- output-direction counterpart to 'fromEnumC', for a {#fun#} returning a bare enum value
  354 -- (not through a pointer out-param, see 'peekEnum' for that case) across the same module-dep boundary
  355 toEnumC :: (Enum a, Integral b) => b -> a
  356 toEnumC = toEnum . fromIntegral
  357 
  358 uncurryNested :: (a -> b -> c -> d) -> (a, (b, c)) -> d
  359 uncurryNested f (x, (y, z)) = f x y z
  360 
  361 -- |'Prelude' only goes up to 'zipWith3'; this fills the gap for unpacking a C-side
  362 -- structure-of-parallel-arrays result into one Haskell record/tuple per element.
  363 zipWith6 :: (a -> b -> c -> d -> e -> f -> g) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g]
  364 zipWith6 f (a:as) (b:bs) (c:cs) (d:ds) (e:es) (g:gs) = f a b c d e g : zipWith6 f as bs cs ds es gs
  365 zipWith6 _ _ _ _ _ _ _ = []
  366 
  367 -- vim: set ff=unix ts=8 sts=2 sw=2 et: