Commit 8dd4f405 authored by Sylvain Henry's avatar Sylvain Henry Committed by Marge Bot
Browse files

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.
parent 12c06927
......@@ -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;
}
......
......@@ -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
......@@ -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.
......@@ -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
......
......@@ -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##
......@@ -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 | #)
......@@ -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
......@@ -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#
......@@ -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#
......
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE MagicHash #-}
module Main (main) where
import Data.List (group)
......@@ -7,22 +10,33 @@ import Control.Monad
import GHC.Word
import GHC.Base
import GHC.Natural
import GHC.Num.Natural
import GHC.Num.Integer
integerPowMod :: Integer -> Integer -> Integer -> Maybe Integer
integerPowMod b e m = case integerPowMod# b e (fromIntegral m) of
(# | () #) -> Nothing
(# r | #) -> Just (fromIntegral r)
main :: IO ()
main = do
print $ powModNatural b e m
print $ powModNatural b e (m-1)
print $ naturalPowMod b e m
print $ naturalPowMod b e (m-1)
print $ integerPowMod b e m
print $ integerPowMod b e (m-1)
print $ integerPowMod b (-e) m
print $ integerPowMod b (-e) (m-1)
print $ integerPowMod (-b) e m
print $ integerPowMod (-b) e (m-1)
print $ integerPowMod (-b) (-e) m
print $ integerPowMod (-b) (-e) (m-1)
where
b,e,m :: Num a => a
b = 2988348162058574136915891421498819466320163312926952423791023078876139
e = 2351399303373464486466122544523690094744975233415544072992656881240319
m = 10^(40::Int)
x = 5328841272400314897981163497728751426
y = 32052182750761975518649228050096851724
b1024 = roll (map fromIntegral (take 128 [0x80::Int .. ]))
roll :: [Word8] -> Integer
roll = GHC.Base.foldr (\b a -> a `shiftL` 8 .|. fromIntegral b) 0
1527229998585248450016808958343740453059
682382427572745901624116300491295556924
Just 1527229998585248450016808958343740453059
Just 682382427572745901624116300491295556924
Just 9710805908340105786808462779613285007339
Nothing
Just 8472770001414751549983191041656259546941
Just 9317617572427254098375883699508704443075
Just 289194091659894213191537220386714992661
Nothing
......@@ -11,9 +11,10 @@ import Control.Monad
import GHC.Word
import GHC.Base
import GHC.Num.Integer
import GHC.Num.Natural
import qualified GHC.Num.Integer as I
recipModInteger :: Integer -> Integer -> Maybe Integer
recipModInteger :: Integer -> Natural -> Maybe Natural
recipModInteger x m = case I.integerRecipMod# x m of
(# y | #) -> Just y
(# | () #) -> Nothing
......@@ -24,16 +25,9 @@ main = do
f x = case recipModInteger x (2*3*11*11*17*17) of
y -> fmap (x,) y
g x = case recipModInteger x (-2*3*11*11*17*17) of
y -> fmap (x,) y
-- positive modulo
print $ mapMaybe f [-7..71]
-- negative modulo
print $ mapMaybe g [-7..71]
-- modulo == 1, -1 or 0
-- modulo == 1 or 0
print (recipModInteger 77 1)
print (recipModInteger 77 (-1))
print (recipModInteger 77 0)
[(-7,149867),(-5,167851),(-1,209813),(1,1),(5,41963),(7,59947),(13,177535),(19,143557),(23,182447),(25,134281),(29,7235),(31,33841),(35,95915),(37,113413),(41,61409),(43,24397),(47,174101),(49,158431),(53,193979),(59,188477),(61,185737),(65,35507),(67,118999),(71,186173)]
[(-7,149867),(-5,167851),(-1,209813),(1,1),(5,41963),(7,59947),(13,177535),(19,143557),(23,182447),(25,134281),(29,7235),(31,33841),(35,95915),(37,113413),(41,61409),(43,24397),(47,174101),(49,158431),(53,193979),(59,188477),(61,185737),(65,35507),(67,118999),(71,186173)]
Nothing
Nothing
Nothing
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment