diff options
author | Sylvain Henry <sylvain@haskus.fr> | 2020-10-01 15:13:18 +0200 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2020-10-02 13:52:38 -0400 |
commit | 8dd4f40512bb18e296280acde0507b4233a27b69 (patch) | |
tree | d6eeb56964c495c8cccff53c91ec0e345de8a06f /libraries | |
parent | 12c06927a03a2fdb516f7008c57d68568b02b576 (diff) | |
download | haskell-8dd4f40512bb18e296280acde0507b4233a27b69.tar.gz |
Bignum: implement integerPowMod (#18427)
Incidentally fix powModInteger which was crashing in integer-gmp for
negative exponents when the modular multiplicative inverse for the base
didn't exist. Now we compute it explicitly with integerRecipMod so that
every backend returns the same result without crashing.
Diffstat (limited to 'libraries')
-rw-r--r-- | libraries/ghc-bignum/cbits/gmp_wrappers.c | 16 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs | 21 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs | 22 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs | 42 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs | 33 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Integer.hs | 38 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot | 3 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Natural.hs-boot | 6 | ||||
-rw-r--r-- | libraries/integer-gmp/src/GHC/Integer/GMP/Internals.hs | 11 |
9 files changed, 154 insertions, 38 deletions
diff --git a/libraries/ghc-bignum/cbits/gmp_wrappers.c b/libraries/ghc-bignum/cbits/gmp_wrappers.c index e036f05a9a..5c12346500 100644 --- a/libraries/ghc-bignum/cbits/gmp_wrappers.c +++ b/libraries/ghc-bignum/cbits/gmp_wrappers.c @@ -632,8 +632,7 @@ integer_gmp_powm(mp_limb_t rp[], // result assert(!mp_limb_zero_p(mp,mn)); if ((mn == 1 || mn == -1) && mp[0] == 1) { - rp[0] = 0; - return 1; + return 0; } if (mp_limb_zero_p(ep,en)) { @@ -659,11 +658,6 @@ integer_gmp_powm(mp_limb_t rp[], // result mpz_clear (r); - if (!rn) { - rp[0] = 0; - return 1; - } - return rn; } @@ -720,8 +714,7 @@ integer_gmp_powm_sec(mp_limb_t rp[], // result assert(mp[0] & 1); if ((mn == 1 || mn == -1) && mp[0] == 1) { - rp[0] = 0; - return 1; + return 0; } if (mp_limb_zero_p(ep,en)) { @@ -753,11 +746,6 @@ integer_gmp_powm_sec(mp_limb_t rp[], // result mpz_clear (r); - if (!rn) { - rp[0] = 0; - return 1; - } - return rn; } diff --git a/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs b/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs index 9e5181c0bb..b23fd1cb21 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs @@ -17,6 +17,7 @@ import GHC.Types import GHC.Num.WordArray import GHC.Num.Primitives import {-# SOURCE #-} GHC.Num.Integer +import {-# SOURCE #-} GHC.Num.Natural import qualified GHC.Num.Backend.Native as Native import qualified GHC.Num.Backend.Selected as Other @@ -472,8 +473,8 @@ integer_gcde a b = integer_recip_mod :: Integer - -> Integer - -> (# Integer | () #) + -> Natural + -> (# Natural | () #) integer_recip_mod x m = let !r0 = Other.integer_recip_mod x m @@ -481,6 +482,20 @@ integer_recip_mod x m = in case (# r0, r1 #) of (# (# | () #), (# | () #) #) -> r0 (# (# y0 | #), (# y1 | #) #) - | isTrue# (integerEq# y0 y1) -> r0 + | isTrue# (naturalEq# y0 y1) -> r0 _ -> case unexpectedValue of !_ -> (# | () #) + +integer_powmod + :: Integer + -> Natural + -> Natural + -> Natural +integer_powmod b e m = + let + !r0 = Other.integer_powmod b e m + !r1 = Native.integer_powmod b e m + in if isTrue# (naturalEq# r0 r1) + then r0 + else case unexpectedValue of + !_ -> naturalZero diff --git a/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs b/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs index 98350d4d29..21d74ee3e4 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs @@ -20,6 +20,8 @@ import GHC.Types import GHC.Num.WordArray import GHC.Num.Primitives import qualified GHC.Num.Backend.Native as Native +import {-# SOURCE #-} GHC.Num.Natural +import {-# SOURCE #-} GHC.Num.Integer default () @@ -606,9 +608,25 @@ integer_gcde = Native.integer_gcde -- with 0 < y < abs m integer_recip_mod :: Integer - -> Integer - -> (# Integer | () #) + -> Natural + -> (# Natural | () #) integer_recip_mod = Native.integer_recip_mod -- for now we use Native's implementation. If some FFI backend user needs a -- specific implementation, we'll need to determine a prototype to pass and -- return BigNat signs and sizes via FFI. + +-- | Computes the modular exponentiation. +-- +-- I.e. y = integer_powmod b e m +-- = b^e `mod` m +-- +-- with 0 <= y < abs m +integer_powmod + :: Integer + -> Natural + -> Natural + -> Natural +integer_powmod = Native.integer_powmod + -- for now we use Native's implementation. If some FFI backend user needs a + -- specific implementation, we'll need to determine a prototype to pass and + -- return BigNat signs and sizes via FFI. diff --git a/libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs b/libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs index 462e67445c..8db1dd57b2 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs @@ -28,6 +28,7 @@ import GHC.Types import GHC.Magic (runRW#) import {-# SOURCE #-} GHC.Num.Integer import {-# SOURCE #-} GHC.Num.BigNat +import {-# SOURCE #-} GHC.Num.Natural default () @@ -354,8 +355,37 @@ bignat_powmod -> WordArray# -> State# RealWorld -> State# RealWorld -bignat_powmod r b e m s = - case ioInt# (integer_gmp_powm# r b (wordArraySize# b) e (wordArraySize# e) m (wordArraySize# m)) s of +bignat_powmod r b e m s = sbignat_powmod r (wordArraySize# b) b e m s + +integer_powmod + :: Integer + -> Natural + -> Natural + -> Natural +integer_powmod b e m = naturalFromBigNat# (withNewWordArray# szm io) + where + !be = naturalToBigNat# e + !bm = naturalToBigNat# m + !(# sb, bb #) = integerToBigNatSign# b + !szb = bigNatSize# bb + !szm = bigNatSize# bm + !ssb = case sb of -- signed size of b + 0# -> szb + _ -> negateInt# szb + + io r s = sbignat_powmod r ssb bb be bm s + + +sbignat_powmod + :: MutableWordArray# RealWorld + -> Int# + -> WordArray# + -> WordArray# + -> WordArray# + -> State# RealWorld + -> State# RealWorld +sbignat_powmod r b_signed_size b e m s = + case ioInt# (integer_gmp_powm# r b b_signed_size e (wordArraySize# e) m (wordArraySize# m)) s of (# s', n #) -> mwaSetSize# r (narrowGmpSize# n) s' integer_gcde @@ -425,16 +455,16 @@ integer_gcde a b = case runRW# io of (# _, a #) -> a integer_recip_mod :: Integer - -> Integer - -> (# Integer | () #) + -> Natural + -> (# Natural | () #) integer_recip_mod x m = let !(# sign_x, bx #) = integerToBigNatSign# x - !(# _sign_m, bm #) = integerToBigNatSign# m + !bm = naturalToBigNat# m !br = sbignat_recip_mod sign_x bx bm in if isTrue# (bigNatIsZero# br) then (# | () #) - else (# integerFromBigNat# br | #) + else (# naturalFromBigNat# br | #) -- | Return 0 for invalid inputs diff --git a/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs b/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs index c5af604c17..ee031178bd 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs @@ -14,7 +14,7 @@ module GHC.Num.Backend.Native where #include "MachDeps.h" #include "WordSize.h" -#if defined(BIGNUM_NATIVE) || defined(BIGNUM_CHECK) +#if defined(BIGNUM_NATIVE) || defined(BIGNUM_CHECK) || defined(BIGNUM_FFI) import {-# SOURCE #-} GHC.Num.BigNat import {-# SOURCE #-} GHC.Num.Natural import {-# SOURCE #-} GHC.Num.Integer @@ -740,14 +740,37 @@ integer_gcde a b = f (# a,integerOne,integerZero #) (# b,integerZero,integerOne integer_recip_mod :: Integer - -> Integer - -> (# Integer | () #) + -> Natural + -> (# Natural | () #) integer_recip_mod x m = - let m' = integerAbs m + let m' = integerFromNatural m in case integer_gcde x m' of (# g, a, _b #) -- gcd(x,m) = ax+mb = 1 -- ==> ax - 1 = -mb -- ==> ax = 1 [m] - | g `integerEq` integerOne -> (# a `integerMod` m' | #) + | g `integerEq` integerOne -> (# integerToNatural (a `integerMod` m') | #) + -- a `mod` m > 0 because m > 0 | True -> (# | () #) + +integer_powmod + :: Integer + -> Natural + -> Natural + -> Natural +integer_powmod b0 e0 m = go b0 e0 integerOne + where + !m' = integerFromNatural m + + go !b e !r + | isTrue# (e `naturalTestBit#` 0##) + = go b' e' ((r `integerMul` b) `integerMod` m') + + | naturalIsZero e + = integerToNatural r -- r >= 0 by integerMod above + + | True + = go b' e' r + where + b' = (b `integerMul` b) `integerRem` m' + e' = e `naturalShiftR#` 1## diff --git a/libraries/ghc-bignum/src/GHC/Num/Integer.hs b/libraries/ghc-bignum/src/GHC/Num/Integer.hs index 7f0314f859..6ee2d27901 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Integer.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Integer.hs @@ -1233,11 +1233,39 @@ integerGcde a b = case integerGcde# a b of -- integerRecipMod# :: Integer - -> Integer - -> (# Integer | () #) + -> Natural + -> (# Natural | () #) integerRecipMod# x m | integerIsZero x = (# | () #) - | integerIsZero m = (# | () #) - | IS 1# <- m = (# | () #) - | IS (-1#) <- m = (# | () #) + | naturalIsZero m = (# | () #) + | naturalIsOne m = (# | () #) | True = Backend.integer_recip_mod x m + + +-- | Computes the modular exponentiation. +-- +-- I.e. y = integer_powmod b e m +-- = b^e `mod` m +-- +-- with 0 <= y < abs m +-- +-- If e is negative, we use `integerRecipMod#` to try to find a modular +-- multiplicative inverse (which may not exist). +integerPowMod# :: Integer -> Integer -> Natural -> (# Natural | () #) +integerPowMod# !b !e !m + | naturalIsZero m = (# | () #) + | naturalIsOne m = (# naturalZero | #) + | integerIsZero e = (# naturalOne | #) + | integerIsZero b = (# naturalZero | #) + | integerIsOne b = (# naturalOne | #) + -- when the exponent is negative, try to find the modular multiplicative + -- inverse and use it instead + | integerIsNegative e = case integerRecipMod# b m of + (# | () #) -> (# | () #) + (# b' | #) -> integerPowMod# + (integerFromNatural b') + (integerNegate e) + m + + -- e > 0 by cases above + | True = (# Backend.integer_powmod b (integerToNatural e) m | #) diff --git a/libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot b/libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot index e9c2d003ca..80ecd36a34 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot +++ b/libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot @@ -7,6 +7,7 @@ module GHC.Num.Integer where import GHC.Types import GHC.Prim import {-# SOURCE #-} GHC.Num.BigNat +import {-# SOURCE #-} GHC.Num.Natural data Integer @@ -32,3 +33,5 @@ integerQuotRem# :: Integer -> Integer -> (# Integer, Integer #) integerToBigNatSign# :: Integer -> (# Int#, BigNat# #) integerFromBigNatSign# :: Int# -> BigNat# -> Integer integerFromBigNat# :: BigNat# -> Integer +integerToNatural :: Integer -> Natural +integerFromNatural :: Natural -> Integer diff --git a/libraries/ghc-bignum/src/GHC/Num/Natural.hs-boot b/libraries/ghc-bignum/src/GHC/Num/Natural.hs-boot index 964292fa59..57e1a9d9a9 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Natural.hs-boot +++ b/libraries/ghc-bignum/src/GHC/Num/Natural.hs-boot @@ -16,8 +16,12 @@ naturalToWord# :: Natural -> Word# naturalFromWord# :: Word# -> Natural naturalFromBigNat# :: BigNat# -> Natural naturalToBigNat# :: Natural -> BigNat# + +naturalZero :: Natural naturalMul :: Natural -> Natural -> Natural naturalRem :: Natural -> Natural -> Natural -naturalIsZero :: Natural -> Bool naturalShiftR# :: Natural -> Word# -> Natural + +naturalIsZero :: Natural -> Bool naturalTestBit# :: Natural -> Word# -> Bool# +naturalEq# :: Natural -> Natural -> Bool# diff --git a/libraries/integer-gmp/src/GHC/Integer/GMP/Internals.hs b/libraries/integer-gmp/src/GHC/Integer/GMP/Internals.hs index cc1068ba10..2fcb0750ed 100644 --- a/libraries/integer-gmp/src/GHC/Integer/GMP/Internals.hs +++ b/libraries/integer-gmp/src/GHC/Integer/GMP/Internals.hs @@ -32,6 +32,7 @@ module GHC.Integer.GMP.Internals , gcdExtInteger , lcmInteger , sqrInteger + , powModInteger , recipModInteger -- ** Additional conversion operations to 'Integer' @@ -189,10 +190,16 @@ sqrInteger = I.integerSqr {-# DEPRECATED recipModInteger "Use integerRecipMod# instead" #-} recipModInteger :: Integer -> Integer -> Integer -recipModInteger x m = case I.integerRecipMod# x m of - (# y | #) -> y +recipModInteger x m = case I.integerRecipMod# x (I.integerToNatural m) of + (# y | #) -> I.integerFromNatural y (# | () #) -> 0 +{-# DEPRECATED powModInteger "Use integerPowMod# instead" #-} +powModInteger :: Integer -> Integer -> Integer -> Integer +powModInteger b e m = case I.integerPowMod# b e (I.integerToNatural m) of + (# r | #) -> I.integerFromNatural r + (# | () #) -> 0 + {-# DEPRECATED wordToNegInteger "Use integerFromWordNeg# instead" #-} wordToNegInteger :: Word# -> Integer wordToNegInteger = I.integerFromWordNeg# |