cryptol-3.4.0: src/GHC/Num/Compat.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE UnboxedSums #-}
{-# LANGUAGE UnboxedTuples #-}
-- |
-- Module : GHC.Num.Compat
-- Description : Defines numeric compatibility shims that work with both
-- ghc-bignum (GHC 9.0+) and integer-gmp (older GHCs).
-- Copyright : (c) 2021 Galois, Inc.
-- License : BSD3
-- Maintainer : cryptol@galois.com
-- Stability : provisional
-- Portability : portable
module GHC.Num.Compat
( -- * BigNat#
BigNat#
, bigNatAdd
, bigNatIsOne
, bigNatIsZero
, bigNatMul
, bigNatRem
, bigNatSqr
, bigNatSub
, bigNatSubUnsafe
, oneBigNat
, recipModBigNat
, shiftLBigNat
, shiftRBigNat
, testBitBigNat
, zeroBigNat
-- * Integer
, Integer(IS, IP, IN)
, integerRecipMod
-- * Conversions
, bigNatToInteger
, bigNatToNegInteger
, integerToBigNat
) where
#if defined(MIN_VERSION_ghc_bignum)
import GHC.Num.BigNat (BigNat#, bigNatAdd, bigNatIsOne, bigNatIsZero, bigNatMul, bigNatRem, bigNatSqr, bigNatSub, bigNatSubUnsafe)
import qualified GHC.Num.Backend as BN
import qualified GHC.Num.BigNat as BN
import GHC.Num.Integer (Integer(IS, IP, IN))
import qualified GHC.Num.Integer as Integer
import GHC.Exts
-- | Coerce a @BigNat#@ to an integer value.
bigNatToInteger :: BigNat# -> Integer
bigNatToInteger = Integer.integerFromBigNat#
-- | Coerce a @BigNat#@ to a negative integer value.
bigNatToNegInteger :: BigNat# -> Integer
bigNatToNegInteger = Integer.integerFromBigNatNeg#
-- | @'integerRecipMod' x m@ computes the modular inverse of @x@ mod @m@.
--
-- PRECONDITION: @m@ must be strictly positive.
integerRecipMod :: Integer -> Integer -> Maybe Integer
integerRecipMod x y =
case Integer.integerRecipMod# x (Integer.integerToNaturalClamp y) of
(# r | #) -> Just (toInteger r)
(# | () #) -> Nothing
-- | Coerce an integer value to a @BigNat#@. This operation only really makes
-- sense for nonnegative values, but this condition is not checked.
integerToBigNat :: Integer -> BigNat#
integerToBigNat = Integer.integerToBigNatClamp#
-- Top-level unlifted bindings aren't allowed, so we fake one with a thunk.
oneBigNat :: (# #) -> BigNat#
oneBigNat _ = BN.bigNatFromWord# 1##
recipModBigNat :: BigNat# -> BigNat# -> BigNat#
recipModBigNat = BN.sbignat_recip_mod 0#
shiftLBigNat :: BigNat# -> Int# -> BigNat#
shiftLBigNat bn i = BN.bigNatShiftL# bn (int2Word# i)
shiftRBigNat :: BigNat# -> Int# -> BigNat#
shiftRBigNat bn i = BN.bigNatShiftR# bn (int2Word# i)
testBitBigNat :: BigNat# -> Int# -> Bool
testBitBigNat bn i = isTrue# (BN.bigNatTestBit# bn (int2Word# i))
-- Top-level unlifted bindings aren't allowed, so we fake one with a thunk.
zeroBigNat :: (# #) -> BigNat#
zeroBigNat _ = BN.bigNatFromWord# 0##
#else
import GHC.Integer.GMP.Internals (bigNatToInteger, bigNatToNegInteger, recipModBigNat, shiftLBigNat, shiftRBigNat, testBitBigNat)
import qualified GHC.Integer.GMP.Internals as GMP
import GHC.Exts
type BigNat# = GMP.BigNat
{-# COMPLETE IS, IP, IN #-}
pattern IS :: Int# -> Integer
pattern IS i = GMP.S# i
pattern IP :: ByteArray# -> Integer
pattern IP ba = GMP.Jp# (GMP.BN# ba)
pattern IN :: ByteArray# -> Integer
pattern IN ba = GMP.Jn# (GMP.BN# ba)
bigNatAdd :: BigNat# -> BigNat# -> BigNat#
bigNatAdd = GMP.plusBigNat
bigNatIsOne :: BigNat# -> Bool
bigNatIsOne bn = GMP.eqBigNat bn GMP.oneBigNat
bigNatIsZero :: BigNat# -> Bool
bigNatIsZero = GMP.isZeroBigNat
bigNatMul :: BigNat# -> BigNat# -> BigNat#
bigNatMul = GMP.timesBigNat
bigNatRem :: BigNat# -> BigNat# -> BigNat#
bigNatRem = GMP.remBigNat
bigNatSqr :: BigNat# -> BigNat#
bigNatSqr = GMP.sqrBigNat
bigNatSub :: BigNat# -> BigNat# -> (# (# #) | BigNat# #)
bigNatSub x y =
case GMP.isNullBigNat# res of
0# -> (# | res #)
_ -> (# (# #) | #)
where
res = GMP.minusBigNat x y
bigNatSubUnsafe :: BigNat# -> BigNat# -> BigNat#
bigNatSubUnsafe = GMP.minusBigNat
integerToBigNat :: Integer -> BigNat#
integerToBigNat (GMP.S# i) = GMP.wordToBigNat (int2Word# i)
integerToBigNat (GMP.Jp# b) = b
integerToBigNat (GMP.Jn# b) = b
-- | @'integerRecipMod' x m@ computes the modular inverse of @x@ mod @m@.
--
-- PRECONDITION: @m@ must be strictly positive.
integerRecipMod :: Integer -> Integer -> Maybe Integer
integerRecipMod x y
| res == 0 = Nothing
| otherwise = Just res
where
res = GMP.recipModInteger x y
oneBigNat :: (##) -> BigNat#
oneBigNat _ = GMP.oneBigNat
zeroBigNat :: (##) -> BigNat#
zeroBigNat _ = GMP.zeroBigNat
#endif