diff options
author | Sylvain Henry <sylvain@haskus.fr> | 2020-09-24 11:33:33 +0200 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2020-09-25 21:14:36 -0400 |
commit | 04bc50b3c8e40387a0d0f090ea23cd68923f1834 (patch) | |
tree | 56eac59646066c462ef8e3e3dfa92d3e4a0b2ad9 /libraries/ghc-bignum/src/GHC | |
parent | 92daad241bf136a10346ecbf520d62921c82bf7d (diff) | |
download | haskell-04bc50b3c8e40387a0d0f090ea23cd68923f1834.tar.gz |
Bignum: implement extended GCD (#18427)
Diffstat (limited to 'libraries/ghc-bignum/src/GHC')
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs | 16 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs | 17 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs | 81 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs | 20 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/BigNat.hs-boot | 1 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Integer.hs | 47 | ||||
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot | 30 |
7 files changed, 207 insertions, 5 deletions
diff --git a/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs b/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs index 73f8366ad0..a8ee366312 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/Check.hs @@ -16,6 +16,7 @@ import GHC.Prim import GHC.Types import GHC.Num.WordArray import GHC.Num.Primitives +import {-# SOURCE #-} GHC.Num.Integer import qualified GHC.Num.Backend.Native as Native import qualified GHC.Num.Backend.Selected as Other @@ -453,3 +454,18 @@ bignat_powmod_words b e m = in case gr `eqWord#` nr of 1# -> gr _ -> unexpectedValue_Word# (# #) + +integer_gcde + :: Integer + -> Integer + -> (# Integer, Integer, Integer #) +integer_gcde a b = + let + !(# g0,x0,y0 #) = Other.integer_gcde a b + !(# g1,x1,y1 #) = Native.integer_gcde a b + in if isTrue# (integerEq# x0 x1 + &&# integerEq# y0 y1 + &&# integerEq# g0 g1) + then (# g0, x0, y0 #) + else case unexpectedValue of + !_ -> (# integerZero, integerZero, integerZero #) diff --git a/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs b/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs index a049cfe332..3fddd19098 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/FFI.hs @@ -19,6 +19,7 @@ import GHC.Prim import GHC.Types import GHC.Num.WordArray import GHC.Num.Primitives +import qualified GHC.Num.Backend.Native as Native default () @@ -579,3 +580,19 @@ bignat_powmod_words = ghc_bignat_powmod_words foreign import ccall unsafe ghc_bignat_powmod_words :: Word# -> Word# -> Word# -> Word# + +-- | Return extended GCD of two non-zero integers. +-- +-- I.e. integer_gcde a b returns (g,x,y) so that ax + by = g +-- +-- Input: a and b are non zero. +-- Output: g must be > 0 +-- +integer_gcde + :: Integer + -> Integer + -> (# Integer, Integer, Integer #) +integer_gcde = Native.integer_gcde + -- 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 a340db573e..10242dfdc7 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/GMP.hs @@ -8,6 +8,9 @@ {-# LANGUAGE UnliftedFFITypes #-} {-# LANGUAGE NegativeLiterals #-} {-# LANGUAGE BlockArguments #-} +{-# LANGUAGE LambdaCase #-} + +{-# OPTIONS_GHC -Wno-name-shadowing #-} -- | Backend based on the GNU GMP library. -- @@ -22,6 +25,9 @@ import GHC.Num.WordArray import GHC.Num.Primitives import GHC.Prim import GHC.Types +import GHC.Magic (runRW#) +import {-# SOURCE #-} GHC.Num.Integer +import {-# SOURCE #-} GHC.Num.BigNat default () @@ -352,6 +358,70 @@ bignat_powmod r b e m s = case ioInt# (integer_gmp_powm# r b (wordArraySize# b) e (wordArraySize# e) m (wordArraySize# m)) s of (# s', n #) -> mwaSetSize# r (narrowGmpSize# n) s' +integer_gcde + :: Integer + -> Integer + -> (# Integer, Integer, Integer #) +integer_gcde a b = case runRW# io of (# _, a #) -> a + where + !(# sa, ba #) = integerToBigNatSign# a + !(# sb, bb #) = integerToBigNatSign# b + !sza = bigNatSize# ba + !szb = bigNatSize# bb + -- signed sizes of a and b + !ssa = case sa of + 0# -> sza + _ -> negateInt# sza + !ssb = case sb of + 0# -> szb + _ -> negateInt# szb + + -- gcd(a,b) < min(a,b) + !g_init_sz = minI# sza szb + + -- According to https://gmplib.org/manual/Number-Theoretic-Functions.html#index-mpz_005fgcdext + -- a*x + b*y = g + -- abs(x) < abs(b) / (2 g) < abs(b) + -- abs(y) < abs(a) / (2 g) < abs(a) + !x_init_sz = szb + !y_init_sz = sza + + io s = + -- allocate output arrays + case newWordArray# g_init_sz s of { (# s, mbg #) -> + case newWordArray# x_init_sz s of { (# s, mbx #) -> + case newWordArray# y_init_sz s of { (# s, mby #) -> + -- allocate space to return sizes (3x4 = 12) + case newPinnedByteArray# 12# s of { (# s, mszs #) -> + case unsafeFreezeByteArray# mszs s of { (# s, szs #) -> + let !ssx_ptr = byteArrayContents# szs in + let !ssy_ptr = ssx_ptr `plusAddr#` 4# in + let !sg_ptr = ssy_ptr `plusAddr#` 4# in + -- call GMP + case ioVoid (integer_gmp_gcdext# mbx ssx_ptr mby ssy_ptr mbg sg_ptr ba ssa bb ssb) s of { s -> + -- read sizes + case readInt32OffAddr# ssx_ptr 0# s of { (# s, ssx #) -> + case readInt32OffAddr# ssy_ptr 0# s of { (# s, ssy #) -> + case readInt32OffAddr# sg_ptr 0# s of { (# s, sg #) -> + case touch# szs s of { s -> + -- shrink x, y and g to their actual sizes and freeze them + let !sx = absI# ssx in + let !sy = absI# ssy in + case mwaSetSize# mbx sx s of { s -> + case mwaSetSize# mby sy s of { s -> + case mwaSetSize# mbg sg s of { s -> + + -- return x, y and g as Integer + case unsafeFreezeByteArray# mbx s of { (# s, bx #) -> + case unsafeFreezeByteArray# mby s of { (# s, by #) -> + case unsafeFreezeByteArray# mbg s of { (# s, bg #) -> + + (# s, (# integerFromBigNat# bg + , integerFromBigNatSign# (ssx <# 0#) bx + , integerFromBigNatSign# (ssy <# 0#) by #) #) + }}}}}}}}}}}}}}}} + + ---------------------------------------------------------------------- -- FFI ccall imports @@ -366,10 +436,13 @@ foreign import ccall unsafe "integer_gmp_mpn_gcd" c_mpn_gcd# :: MutableByteArray# s -> ByteArray# -> GmpSize# -> ByteArray# -> GmpSize# -> IO GmpSize -foreign import ccall unsafe "integer_gmp_gcdext" - integer_gmp_gcdext# :: MutableByteArray# s -> MutableByteArray# s - -> ByteArray# -> GmpSize# - -> ByteArray# -> GmpSize# -> IO GmpSize +foreign import ccall unsafe "integer_gmp_gcdext" integer_gmp_gcdext# + :: MutableByteArray# s -> Addr# + -> MutableByteArray# s -> Addr# + -> MutableByteArray# s -> Addr# + -> ByteArray# -> GmpSize# + -> ByteArray# -> GmpSize# + -> IO () -- mp_limb_t mpn_add_1 (mp_limb_t *rp, const mp_limb_t *s1p, mp_size_t n, -- mp_limb_t s2limb) diff --git a/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs b/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs index 1169af41d6..db4196f51f 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Backend/Native.hs @@ -17,9 +17,11 @@ module GHC.Num.Backend.Native where #if defined(BIGNUM_NATIVE) || defined(BIGNUM_CHECK) import {-# SOURCE #-} GHC.Num.BigNat import {-# SOURCE #-} GHC.Num.Natural +import {-# SOURCE #-} GHC.Num.Integer #else import GHC.Num.BigNat import GHC.Num.Natural +import GHC.Num.Integer #endif import GHC.Num.WordArray import GHC.Num.Primitives @@ -717,3 +719,21 @@ bignat_powmod_words b e m = bignat_powmod_word (wordArrayFromWord# b) (wordArrayFromWord# e) m + + +integer_gcde + :: Integer + -> Integer + -> (# Integer, Integer, Integer #) +integer_gcde a b = f (# a,integerOne,integerZero #) (# b,integerZero,integerOne #) + where + -- returned "g" must be positive + fix (# g, x, y #) + | integerIsNegative g = (# integerNegate g, integerNegate x, integerNegate y #) + | True = (# g,x,y #) + + f old@(# old_g, old_s, old_t #) new@(# g, s, t #) + | integerIsZero g = fix old + | True = case integerQuotRem# old_g g of + !(# q, r #) -> f new (# r , old_s `integerSub` (q `integerMul` s) + , old_t `integerSub` (q `integerMul` t) #) diff --git a/libraries/ghc-bignum/src/GHC/Num/BigNat.hs-boot b/libraries/ghc-bignum/src/GHC/Num/BigNat.hs-boot index 90a3f11ea1..37edb97582 100644 --- a/libraries/ghc-bignum/src/GHC/Num/BigNat.hs-boot +++ b/libraries/ghc-bignum/src/GHC/Num/BigNat.hs-boot @@ -10,6 +10,7 @@ import GHC.Prim type BigNat# = WordArray# data BigNat = BN# { unBigNat :: BigNat# } +bigNatSize# :: BigNat# -> Int# bigNatSubUnsafe :: BigNat# -> BigNat# -> BigNat# bigNatMulWord# :: BigNat# -> Word# -> BigNat# bigNatRem :: BigNat# -> BigNat# -> BigNat# diff --git a/libraries/ghc-bignum/src/GHC/Num/Integer.hs b/libraries/ghc-bignum/src/GHC/Num/Integer.hs index 04dd57b291..43e4a18cdd 100644 --- a/libraries/ghc-bignum/src/GHC/Num/Integer.hs +++ b/libraries/ghc-bignum/src/GHC/Num/Integer.hs @@ -6,6 +6,7 @@ {-# LANGUAGE NegativeLiterals #-} {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE BlockArguments #-} +{-# LANGUAGE LambdaCase #-} -- | -- Module : GHC.Num.Integer @@ -31,6 +32,7 @@ import GHC.Magic import GHC.Num.Primitives import GHC.Num.BigNat import GHC.Num.Natural +import qualified GHC.Num.Backend as Backend #if WORD_SIZE_IN_BITS < 64 import GHC.IntWord64 @@ -113,6 +115,17 @@ integerFromBigNatSign# !sign !bn | True = integerFromBigNatNeg# bn +-- | Convert an Integer into a sign-bit and a BigNat +integerToBigNatSign# :: Integer -> (# Int#, BigNat# #) +integerToBigNatSign# = \case + IS x + | isTrue# (x >=# 0#) + -> (# 0#, bigNatFromWord# (int2Word# x) #) + | True + -> (# 1#, bigNatFromWord# (int2Word# (negateInt# x)) #) + IP x -> (# 0#, x #) + IN x -> (# 1#, x #) + -- | Convert an Integer into a BigNat. -- -- Return 0 for negative Integers. @@ -853,7 +866,7 @@ integerDivMod# :: Integer -> Integer -> (# Integer, Integer #) {-# NOINLINE integerDivMod# #-} integerDivMod# !n !d | isTrue# (integerSignum# r ==# negateInt# (integerSignum# d)) - = let !q' = integerAdd q (IS -1#) -- TODO: optimize + = let !q' = integerSub q (IS 1#) !r' = integerAdd r d in (# q', r' #) | True = qr @@ -1169,3 +1182,35 @@ integerFromByteArray# sz ba off e s = case bigNatFromByteArray# sz ba off e s of integerFromByteArray :: Word# -> ByteArray# -> Word# -> Bool# -> Integer integerFromByteArray sz ba off e = case runRW# (integerFromByteArray# sz ba off e) of (# _, i #) -> i + + +-- | Get the extended GCD of two integers. +-- +-- `integerGcde# a b` returns (# g,x,y #) where +-- * ax + by = g = |gcd a b| +integerGcde# + :: Integer + -> Integer + -> (# Integer, Integer, Integer #) +integerGcde# a b + | integerIsZero a && integerIsZero b = (# integerZero, integerZero, integerZero #) + | integerIsZero a = fix (# b , integerZero, integerOne #) + | integerIsZero b = fix (# a , integerOne, integerZero #) + | integerAbs a `integerEq` integerAbs b = fix (# b , integerZero, integerOne #) + | True = Backend.integer_gcde a b + where + -- returned "g" must be positive + fix (# g, x, y #) + | integerIsNegative g = (# integerNegate g, integerNegate x, integerNegate y #) + | True = (# g,x,y #) + +-- | Get the extended GCD of two integers. +-- +-- `integerGcde a b` returns (g,x,y) where +-- * ax + by = g = |gcd a b| +integerGcde + :: Integer + -> Integer + -> ( Integer, Integer, Integer) +integerGcde a b = case integerGcde# a b of + (# g,x,y #) -> (g,x,y) diff --git a/libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot b/libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot new file mode 100644 index 0000000000..a07b9b7548 --- /dev/null +++ b/libraries/ghc-bignum/src/GHC/Num/Integer.hs-boot @@ -0,0 +1,30 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE MagicHash #-} + +module GHC.Num.Integer where + +import GHC.Types +import GHC.Prim +import {-# SOURCE #-} GHC.Num.BigNat + +data Integer + +integerZero :: Integer +integerOne :: Integer + +integerEq# :: Integer -> Integer -> Int# +integerEq :: Integer -> Integer -> Bool +integerGt :: Integer -> Integer -> Bool +integerIsZero :: Integer -> Bool +integerIsNegative :: Integer -> Bool + +integerSub :: Integer -> Integer -> Integer +integerMul :: Integer -> Integer -> Integer +integerNegate :: Integer -> Integer +integerDivMod# :: Integer -> Integer -> (# Integer, Integer #) +integerQuotRem# :: Integer -> Integer -> (# Integer, Integer #) + +integerToBigNatSign# :: Integer -> (# Int#, BigNat# #) +integerFromBigNatSign# :: Int# -> BigNat# -> Integer +integerFromBigNat# :: BigNat# -> Integer |