packages feed

sbv-14.8: Data/SBV/Compilers/C/NonLinear.hs

-----------------------------------------------------------------------------
-- |
-- Module    : Data.SBV.Compilers.C.NonLinear
-- Copyright : (c) Levent Erkok
-- License   : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Lowering of transcendental real operations and mapped integer powers.
-----------------------------------------------------------------------------

{-# OPTIONS_GHC -Wall -Werror #-}

module Data.SBV.Compilers.C.NonLinear
  ( nonLinearExpr
  , mappedIntegerPowerRuntime
  ) where

import Text.PrettyPrint.HughesPJ
import qualified Text.PrettyPrint.HughesPJ as P ((<>))

import Data.SBV.Compilers.C.Lowering (CLowering, CRequirement(..), expressionLowering)
import Data.SBV.Compilers.CodeGen    (CgConfig(..), CgSRealType(..))
import Data.SBV.Core.Data
import Data.SBV.Core.Symbolic        (NROp(..))

-- | Lower mapped-real transcendental operations and mapped integer
-- exponentiation. Exact integer exponentiation delegates to the GMP lowering
-- stage, while transcendental operations over exact rational reals receive an
-- actionable diagnostic.
nonLinearExpr :: CgConfig -> Op -> [SV] -> Kind -> [Doc] -> Maybe CLowering
nonLinearExpr cfg (NonLinear nonLinearOp) svs resultKind args
  | nonLinearOp == NR_IntPow
  = integerPower
  | resultKind == KReal && all ((== KReal) . kindOf) svs
  = realOperation
  | True
  = unsupported
 where integerPower
         | resultKind == KUnbounded
         , map kindOf svs == [KUnbounded, KUnbounded]
         , [base, powerValue] <- args
         = case cgInteger cfg of
             Nothing    -> Nothing
             Just width -> Just $ expressionLowering [CRequiresIntegerPower]
                                $ namedCall ("sbv_integer_pow_s" ++ show width) [base, powerValue]
         | True
         = unsupported

       realOperation = case cgReal cfg of
         Nothing       -> error $ "SBV->C: The exact GMP-rational SReal representation cannot represent "
                                ++ nonLinearName nonLinearOp
                                ++ ". Select a native approximation with cgSRealType to compile this operation."
         Just realType -> Just $ expressionLowering [CRequiresLibM] (renderReal realType)

       renderReal realType = case (nonLinearOp, args) of
         (NR_Pow   , [left, right]) -> namedCall (realFunction realType "pow") [left, right]
         (NR_IntPow, _            ) -> unsupportedDoc
         (_        , [value]       ) -> namedCall (realFunction realType (nonLinearName nonLinearOp)) [value]
         _                         -> unsupportedDoc

       unsupportedDoc = error unsupportedMessage
       unsupported    = error unsupportedMessage

       unsupportedMessage = "SBV->C: Cannot lower " ++ nonLinearName nonLinearOp
                         ++ " with argument kinds " ++ show (map kindOf svs)
                         ++ " and result kind " ++ show resultKind

       namedCall functionName callArgs = text functionName P.<> parens (fsep (punctuate comma callArgs))

       realFunction CgFloat      baseName = baseName ++ "f"
       realFunction CgDouble     baseName = baseName
       realFunction CgLongDouble baseName = baseName ++ "l"

       nonLinearName NR_Sin    = "sin"
       nonLinearName NR_Cos    = "cos"
       nonLinearName NR_Tan    = "tan"
       nonLinearName NR_ASin   = "asin"
       nonLinearName NR_ACos   = "acos"
       nonLinearName NR_ATan   = "atan"
       nonLinearName NR_Sqrt   = "sqrt"
       nonLinearName NR_Sinh   = "sinh"
       nonLinearName NR_Cosh   = "cosh"
       nonLinearName NR_Tanh   = "tanh"
       nonLinearName NR_Exp    = "exp"
       nonLinearName NR_Log    = "log"
       nonLinearName NR_Pow    = "pow"
       nonLinearName NR_IntPow = "integer exponentiation"
nonLinearExpr _ _ _ _ _ = Nothing

-- | Emit modular exponentiation for an explicitly bounded C representation
-- of 'SInteger'. Negative exponents follow SMT-LIB integer-power semantics;
-- nonnegative results wrap through the selected two's-complement width.
mappedIntegerPowerRuntime :: CgConfig -> Doc
mappedIntegerPowerRuntime cfg = case cgInteger cfg of
  Nothing    -> error "SBV->C: Mapped integer-power runtime requested for exact SInteger"
  Just width -> text . unlines $
    [ "static " ++ signedType ++ " " ++ helperName ++ "(" ++ signedType ++ " base, " ++ signedType ++ " exponent)"
    , "{"
    , "  " ++ unsignedType ++ " result_bits = (" ++ unsignedType ++ ") 1, factor_bits, power;"
    , "  " ++ signedType ++ " result;"
    , "  if (exponent < 0) {"
    , "    if (base == 1) return (" ++ signedType ++ ") 1;"
    , "    if (base == -1) return ((" ++ unsignedType ++ ") exponent & (" ++ unsignedType ++ ") 1) != 0 ? (" ++ signedType ++ ") -1 : (" ++ signedType ++ ") 1;"
    , "    return (" ++ signedType ++ ") 0;"
    , "  }"
    , "  memcpy(&factor_bits, &base, sizeof factor_bits); power = (" ++ unsignedType ++ ") exponent;"
    , "  while (power != 0) {"
    , "    if ((power & (" ++ unsignedType ++ ") 1) != 0) result_bits = (" ++ unsignedType ++ ") ((uint64_t) result_bits * (uint64_t) factor_bits);"
    , "    power = (" ++ unsignedType ++ ") (power >> 1);"
    , "    if (power != 0) factor_bits = (" ++ unsignedType ++ ") ((uint64_t) factor_bits * (uint64_t) factor_bits);"
    , "  }"
    , "  memcpy(&result, &result_bits, sizeof result); return result;"
    , "}"
    , ""
    ]
    where signedType   = "SInt"  ++ show width
          unsignedType = "SWord" ++ show width
          helperName   = "sbv_integer_pow_s" ++ show width