diff --git a/GHC/Integer/GMP/Internals.hs b/GHC/Integer/GMP/Internals.hs
index 4ad1f6290991615946fdc1013483c7ebb35c6c1b..4a7ff5d3bcd65dc73ca37dba458c3e5d3cf638d7 100644
--- a/GHC/Integer/GMP/Internals.hs
+++ b/GHC/Integer/GMP/Internals.hs
@@ -1,6 +1,6 @@
{-# LANGUAGE NoImplicitPrelude #-}
-module GHC.Integer.GMP.Internals (Integer(..), gcdInt, gcdInteger, lcmInteger)
+module GHC.Integer.GMP.Internals (Integer(..), gcdInt, gcdInteger, lcmInteger, powInteger, powModInteger, recipModInteger)
where
import GHC.Integer.Type
diff --git a/GHC/Integer/GMP/Prim.hs b/GHC/Integer/GMP/Prim.hs
index 7c28ce2c2bde4908222bb2259f6b8a90c548619e..59aa6f4b906078b714e511cb84c28f1668e70564 100644
--- a/GHC/Integer/GMP/Prim.hs
+++ b/GHC/Integer/GMP/Prim.hs
@@ -41,6 +41,10 @@ module GHC.Integer.GMP.Prim (
mul2ExpInteger#,
fdivQ2ExpInteger#,
+ powInteger#,
+ powModInteger#,
+ recipModInteger#,
+
#if WORD_SIZE_IN_BITS < 64
int64ToInteger#, integerToInt64#,
word64ToInteger#, integerToWord64#,
@@ -177,6 +181,21 @@ foreign import prim "integer_cmm_mul2ExpIntegerzh" mul2ExpInteger#
foreign import prim "integer_cmm_fdivQ2ExpIntegerzh" fdivQ2ExpInteger#
:: Int# -> ByteArray# -> Int# -> (# Int#, ByteArray# #)
+-- |
+--
+foreign import prim "integer_cmm_powIntegerzh" powInteger#
+ :: Int# -> ByteArray# -> Word# -> (# Int#, ByteArray# #)
+
+-- |
+--
+foreign import prim "integer_cmm_powModIntegerzh" powModInteger#
+ :: Int# -> ByteArray# -> Int# -> ByteArray# -> Int# -> ByteArray# -> (# Int#, ByteArray# #)
+
+-- |
+--
+foreign import prim "integer_cmm_recipModIntegerzh" recipModInteger#
+ :: Int# -> ByteArray# -> Int# -> ByteArray# -> (# Int#, ByteArray# #)
+
-- |
--
foreign import prim "integer_cmm_complementIntegerzh" complementInteger#
diff --git a/GHC/Integer/Type.lhs b/GHC/Integer/Type.lhs
index a01542cccaee9c60b3d86356a84c3cf0b5aa5374..554160c0a62b79fd24fb7fbe1cfb156b5b5dfab0 100644
--- a/GHC/Integer/Type.lhs
+++ b/GHC/Integer/Type.lhs
@@ -45,6 +45,7 @@ import GHC.Integer.GMP.Prim (
int2Integer#, integer2Int#, word2Integer#, integer2Word#,
andInteger#, orInteger#, xorInteger#, complementInteger#,
testBitInteger#, mul2ExpInteger#, fdivQ2ExpInteger#,
+ powInteger#, powModInteger#, recipModInteger#,
#if WORD_SIZE_IN_BITS < 64
int64ToInteger#, integerToInt64#,
word64ToInteger#, integerToWord64#,
@@ -581,8 +582,46 @@ shiftRInteger (J# s d) i = case fdivQ2ExpInteger# s d i of
testBitInteger :: Integer -> Int# -> Bool
testBitInteger j@(S# _) i = testBitInteger (toBig j) i
testBitInteger (J# s d) i = isTrue# (testBitInteger# s d i /=# 0#)
+
+-- | @powInteger b e@ computes base @b@ raised to exponent @e@.
+{-# NOINLINE powInteger #-}
+powInteger :: Integer -> Word# -> Integer
+powInteger j@(S# _) e = powInteger (toBig j) e
+powInteger (J# s d) e = case powInteger# s d e of
+ (# s', d' #) -> J# s' d'
+
+-- | @powModInteger b e m@ computes base @b@ raised to exponent @e@
+-- modulo @m@.
+--
+-- Negative exponents are supported if an inverse modulo @m@
+-- exists. It's advised to avoid calling this primitive with negative
+-- exponents unless it is guaranteed the inverse exists, as failure to
+-- do so will likely cause program abortion due to a divide-by-zero
+-- fault. See also 'recipModInteger'.
+{-# NOINLINE powModInteger #-}
+powModInteger :: Integer -> Integer -> Integer -> Integer
+powModInteger (J# s1 d1) (J# s2 d2) (J# s3 d3) =
+ case powModInteger# s1 d1 s2 d2 s3 d3 of
+ (# s', d' #) -> J# s' d'
+powModInteger b e m = powModInteger (toBig b) (toBig e) (toBig m)
+
+-- | @recipModInteger x m@ computes the inverse of @x@ modulo @m@. If
+-- the inverse exists, the return value @y@ will satisfy @0 < y <
+-- abs(m)@, otherwise the result is 0.
+--
+-- Note: The implementation exploits the undocumented property of
+-- @mpz_invert()@ to not mangle the result operand (which is initialized
+-- to 0) in case of non-existence of the inverse.
+{-# NOINLINE recipModInteger #-}
+recipModInteger :: Integer -> Integer -> Integer
+recipModInteger j@(S# _) m@(S# _) = recipModInteger (toBig j) (toBig m)
+recipModInteger j@(S# _) m@(J# _ _) = recipModInteger (toBig j) m
+recipModInteger j@(J# _ _) m@(S# _) = recipModInteger j (toBig m)
+recipModInteger (J# s d) (J# ms md) = case recipModInteger# s d ms md of
+ (# s', d' #) -> J# s' d'
\end{code}
+
%*********************************************************
%* *
\subsection{The @Integer@ hashing@}
diff --git a/cbits/gmp-wrappers.cmm b/cbits/gmp-wrappers.cmm
index 8a201f12d002a55a57796143adb7cc501bd15382..5c7bb0b7257d5771fc733252b77f9ba84b73f719 100644
--- a/cbits/gmp-wrappers.cmm
+++ b/cbits/gmp-wrappers.cmm
@@ -49,6 +49,9 @@ import "integer-gmp" __gmpz_and;
import "integer-gmp" __gmpz_xor;
import "integer-gmp" __gmpz_ior;
import "integer-gmp" __gmpz_com;
+import "integer-gmp" __gmpz_pow_ui;
+import "integer-gmp" __gmpz_powm;
+import "integer-gmp" __gmpz_invert;
import "integer-gmp" integer_cbits_decodeDouble;
@@ -246,6 +249,47 @@ again: \
MP_INT__mp_d(mp_result1) - SIZEOF_StgArrWords); \
}
+#define GMP_TAKE3_RET1(name,mp_fun) \
+name (W_ ws1, P_ d1, W_ ws2, P_ d2, W_ ws3, P_ d3) \
+{ \
+ CInt s1, s2, s3; \
+ W_ mp_tmp1; \
+ W_ mp_tmp2; \
+ W_ mp_tmp3; \
+ W_ mp_result1; \
+ \
+again: \
+ STK_CHK_GEN_N (4 * SIZEOF_MP_INT); \
+ MAYBE_GC(again); \
+ \
+ s1 = W_TO_INT(ws1); \
+ s2 = W_TO_INT(ws2); \
+ s3 = W_TO_INT(ws3); \
+ \
+ mp_tmp1 = Sp - 1 * SIZEOF_MP_INT; \
+ mp_tmp2 = Sp - 2 * SIZEOF_MP_INT; \
+ mp_tmp3 = Sp - 3 * SIZEOF_MP_INT; \
+ mp_result1 = Sp - 4 * SIZEOF_MP_INT; \
+ MP_INT__mp_alloc(mp_tmp1) = W_TO_INT(BYTE_ARR_WDS(d1)); \
+ MP_INT__mp_size(mp_tmp1) = (s1); \
+ MP_INT__mp_d(mp_tmp1) = BYTE_ARR_CTS(d1); \
+ MP_INT__mp_alloc(mp_tmp2) = W_TO_INT(BYTE_ARR_WDS(d2)); \
+ MP_INT__mp_size(mp_tmp2) = (s2); \
+ MP_INT__mp_d(mp_tmp2) = BYTE_ARR_CTS(d2); \
+ MP_INT__mp_alloc(mp_tmp3) = W_TO_INT(BYTE_ARR_WDS(d3)); \
+ MP_INT__mp_size(mp_tmp3) = (s3); \
+ MP_INT__mp_d(mp_tmp3) = BYTE_ARR_CTS(d3); \
+ \
+ ccall __gmpz_init(mp_result1 "ptr"); \
+ \
+ /* Perform the operation */ \
+ ccall mp_fun(mp_result1 "ptr",mp_tmp1 "ptr",mp_tmp2 "ptr", \
+ mp_tmp3 "ptr"); \
+ \
+ return (TO_W_(MP_INT__mp_size(mp_result1)), \
+ MP_INT__mp_d(mp_result1) - SIZEOF_StgArrWords); \
+}
+
#define GMP_TAKE1_UL1_RET1(name,mp_fun) \
name (W_ ws1, P_ d1, W_ wul) \
{ \
@@ -389,6 +433,10 @@ GMP_TAKE1_RET1(integer_cmm_complementIntegerzh, __gmpz_com)
GMP_TAKE2_RET2(integer_cmm_quotRemIntegerzh, __gmpz_tdiv_qr)
GMP_TAKE2_RET2(integer_cmm_divModIntegerzh, __gmpz_fdiv_qr)
+GMP_TAKE3_RET1(integer_cmm_powModIntegerzh, __gmpz_powm)
+GMP_TAKE2_RET1(integer_cmm_recipModIntegerzh, __gmpz_invert)
+GMP_TAKE1_UL1_RET1(integer_cmm_powIntegerzh, __gmpz_pow_ui)
+
integer_cmm_gcdIntzh (W_ int1, W_ int2)
{
W_ r;