{-# 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
  , 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 :: BigNat# -> Integer
bigNatToInteger = BigNat# -> Integer
Integer.integerFromBigNat#

-- | @'integerRecipMod' x m@ computes the modular inverse of @x@ mod @m@.
--
-- PRECONDITION: @m@ must be strictly positive.
integerRecipMod :: Integer -> Integer -> Maybe Integer
integerRecipMod :: Integer -> Integer -> Maybe Integer
integerRecipMod Integer
x Integer
y =
  case Integer -> Natural -> (# Natural | () #)
Integer.integerRecipMod# Integer
x (Integer -> Natural
Integer.integerToNaturalClamp Integer
y) of
    (# Natural
r | #)  -> Integer -> Maybe Integer
forall a. a -> Maybe a
Just (Natural -> Integer
forall a. Integral a => a -> Integer
toInteger Natural
r)
    (# | () #) -> Maybe Integer
forall a. Maybe a
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 -> BigNat#
integerToBigNat = Integer -> BigNat#
Integer.integerToBigNatClamp#

-- Top-level unlifted bindings aren't allowed, so we fake one with a thunk.
oneBigNat :: (# #) -> BigNat#
oneBigNat :: (# #) -> BigNat#
oneBigNat (# #)
_ = Word# -> BigNat#
BN.bigNatFromWord# Word#
1##

recipModBigNat :: BigNat# -> BigNat# -> BigNat#
recipModBigNat :: BigNat# -> BigNat# -> BigNat#
recipModBigNat = Int# -> BigNat# -> BigNat# -> BigNat#
BN.sbignat_recip_mod Int#
0#

shiftLBigNat :: BigNat# -> Int# -> BigNat#
shiftLBigNat :: BigNat# -> Int# -> BigNat#
shiftLBigNat BigNat#
bn Int#
i = BigNat# -> Word# -> BigNat#
BN.bigNatShiftL# BigNat#
bn (Int# -> Word#
int2Word# Int#
i)

shiftRBigNat :: BigNat# -> Int# -> BigNat#
shiftRBigNat :: BigNat# -> Int# -> BigNat#
shiftRBigNat BigNat#
bn Int#
i = BigNat# -> Word# -> BigNat#
BN.bigNatShiftR# BigNat#
bn (Int# -> Word#
int2Word# Int#
i)

testBitBigNat :: BigNat# -> Int# -> Bool
testBitBigNat :: BigNat# -> Int# -> Bool
testBitBigNat BigNat#
bn Int#
i = Int# -> Bool
isTrue# (BigNat# -> Word# -> Int#
BN.bigNatTestBit# BigNat#
bn (Int# -> Word#
int2Word# Int#
i))

-- Top-level unlifted bindings aren't allowed, so we fake one with a thunk.
zeroBigNat :: (# #) -> BigNat#
zeroBigNat :: (# #) -> BigNat#
zeroBigNat (# #)
_ = Word# -> BigNat#
BN.bigNatFromWord# Word#
0##
#else
import           GHC.Integer.GMP.Internals (bigNatToInteger, 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