diff options
Diffstat (limited to 'libraries/ghc-bignum/src/GHC/Num/BigNat.hs')
-rw-r--r-- | libraries/ghc-bignum/src/GHC/Num/BigNat.hs | 1509 |
1 files changed, 1509 insertions, 0 deletions
diff --git a/libraries/ghc-bignum/src/GHC/Num/BigNat.hs b/libraries/ghc-bignum/src/GHC/Num/BigNat.hs new file mode 100644 index 0000000000..5d0a9919f5 --- /dev/null +++ b/libraries/ghc-bignum/src/GHC/Num/BigNat.hs @@ -0,0 +1,1509 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE BlockArguments #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE BinaryLiterals #-} +{-# OPTIONS_GHC -Wno-name-shadowing #-} + +-- | Multi-precision natural +module GHC.Num.BigNat where + +#include "MachDeps.h" +#include "WordSize.h" + +import GHC.Prim +import GHC.Types +import GHC.Classes +import GHC.Magic +import GHC.Num.Primitives +import GHC.Num.WordArray + +#if WORD_SIZE_IN_BITS < 64 +import GHC.IntWord64 +#endif + +#if defined(BIGNUM_CHECK) +import GHC.Num.BigNat.Check + +#elif defined(BIGNUM_NATIVE) +import GHC.Num.BigNat.Native + +#elif defined(BIGNUM_FFI) +import GHC.Num.BigNat.FFI + +#elif defined(BIGNUM_GMP) +import GHC.Num.BigNat.GMP + +#else +#error Undefined BigNat backend. Use a flag to select it (e.g. gmp, native, ffi)` +#endif + +default () + +-- | A BigNat +-- +-- Represented as an array of limbs (Word#) stored in little-endian order (Word# +-- themselves use machine order). +-- +-- Invariant (canonical representation): higher Word# is non-zero. +-- As a consequence, zero is represented with a WordArray# whose size is 0. +type BigNat = WordArray# -- we use a type-alias to make Integer/Natural easier to wire-in + +-- | Check that the BigNat is valid +bigNatCheck# :: BigNat -> Bool# +bigNatCheck# bn + | 0# <- bigNatSize# bn = 1# + | 0## <- bigNatIndex# bn (bigNatSize# bn -# 1#) = 0# + | True = 1# + +-- | Check that the BigNat is valid +bigNatCheck :: BigNat -> Bool +bigNatCheck bn = isTrue# (bigNatCheck# bn) + +-- | Number of words in the BigNat +bigNatSize :: BigNat -> Word +bigNatSize bn = W# (int2Word# (bigNatSize# bn)) + +-- | Number of words in the BigNat +bigNatSize# :: BigNat -> Int# +bigNatSize# ba = wordArraySize# ba + +-- Note [Why Void#?] +-- ~~~~~~~~~~~~~~~~~ +-- +-- We can't have top-level BigNat for now because they are unlifted ByteArray# +-- (see #17521). So we use functions that take an empty argument Void# that +-- will be discarded at compile time. + +data BigNatW = BigNatW BigNat + +{-# NOINLINE bigNatZeroW #-} +bigNatZeroW :: BigNatW +bigNatZeroW = BigNatW (withNewWordArray# 0# (\_ s -> s)) + +{-# NOINLINE bigNatOneW #-} +bigNatOneW :: BigNatW +bigNatOneW = BigNatW (bigNatFromWord# 1##) + +-- | BigNat Zero +bigNatZero :: Void# -> BigNat -- cf Note [Why Void#?] +bigNatZero _ = case bigNatZeroW of + BigNatW w -> w + +-- | BigNat one +bigNatOne :: Void# -> BigNat -- cf Note [Why Void#?] +bigNatOne _ = case bigNatOneW of + BigNatW w -> w + +-- | Indicate if a bigNat is zero +bigNatIsZero :: BigNat -> Bool +bigNatIsZero bn = isTrue# (bigNatIsZero# bn) + +-- | Indicate if a bigNat is zero +bigNatIsZero# :: BigNat -> Bool# +bigNatIsZero# ba = wordArraySize# ba ==# 0# + +-- | Indicate if a bigNat is one +bigNatIsOne :: BigNat -> Bool +bigNatIsOne bn = isTrue# (bigNatIsOne# bn) + +-- | Indicate if a bigNat is one +bigNatIsOne# :: BigNat -> Bool# +bigNatIsOne# ba = + wordArraySize# ba ==# 1# + &&# indexWordArray# ba 0# `eqWord#` 1## + +-- | Indicate if a bigNat is two +bigNatIsTwo :: BigNat -> Bool +bigNatIsTwo bn = isTrue# (bigNatIsTwo# bn) + +-- | Indicate if a bigNat is two +bigNatIsTwo# :: BigNat -> Bool# +bigNatIsTwo# ba = + wordArraySize# ba ==# 1# + &&# indexWordArray# ba 0# `eqWord#` 2## + +-- | Indicate if the value is a power of two and which one +bigNatIsPowerOf2# :: BigNat -> (# () | Word# #) +bigNatIsPowerOf2# a + | bigNatIsZero a = (# () | #) + | True = case wordIsPowerOf2# msw of + (# () | #) -> (# () | #) + (# | c #) -> case checkAllZeroes (imax -# 1#) of + 0# -> (# () | #) + _ -> (# | c `plusWord#` + (int2Word# imax `uncheckedShiftL#` WORD_SIZE_BITS_SHIFT#) #) + where + msw = bigNatIndex# a imax + sz = bigNatSize# a + imax = sz -# 1# + checkAllZeroes i + | isTrue# (i <# 0#) = 1# + | True = case bigNatIndex# a i of + 0## -> checkAllZeroes (i -# 1#) + _ -> 0# + +-- | Return the Word# at the given index +bigNatIndex# :: BigNat -> Int# -> Word# +bigNatIndex# x i = indexWordArray# x i + +-- | Return the Word# at the given index +bigNatIndex :: BigNat -> Int# -> Word +bigNatIndex bn i = W# (bigNatIndex# bn i) + +------------------------------------------------- +-- Conversion +------------------------------------------------- + +-- | Create a BigNat from a Word +bigNatFromWord :: Word -> BigNat +bigNatFromWord (W# w) = bigNatFromWord# w + +-- | Create a BigNat from a Word +bigNatFromWord# :: Word# -> BigNat +bigNatFromWord# 0## = bigNatZero void# +bigNatFromWord# w = wordArrayFromWord# w + +-- | Convert a list of non-zero Words (most-significant first) into a BigNat +bigNatFromWordList :: [Word] -> BigNat +bigNatFromWordList (W# 0##:xs) = bigNatFromWordList xs +bigNatFromWordList xs = bigNatFromWordListUnsafe xs + +-- | Convert a list of non-zero Words (most-significant first) into a BigNat +bigNatFromWordList# :: [Word] -> WordArray# +{-# NOINLINE bigNatFromWordList# #-} +bigNatFromWordList# xs = bigNatFromWordList xs + +-- | Return the absolute value of the Int# in a BigNat +bigNatFromAbsInt# :: Int# -> BigNat +bigNatFromAbsInt# i = bigNatFromWord# (wordFromAbsInt# i) + +-- | Convert a list of non-zero Words (most-significant first) into a BigNat. +-- Don't remove most-significant zero words +bigNatFromWordListUnsafe :: [Word] -> BigNat +bigNatFromWordListUnsafe [] = bigNatZero void# +bigNatFromWordListUnsafe xs = + let + length i [] = i + length i (_:ys) = length (i +# 1#) ys + !lxs = length 0# xs + writeWordList _mwa _i [] s = s + writeWordList mwa i (W# w:ws) s = + case mwaWrite# mwa i w s of + s1 -> writeWordList mwa (i -# 1#) ws s1 + in withNewWordArray# lxs \mwa -> + writeWordList mwa (lxs -# 1#) xs + +-- | Convert a BigNat into a list of non-zero Words (most-significant first) +bigNatToWordList :: BigNat -> [Word] +bigNatToWordList bn = go (bigNatSize# bn) + where + go 0# = [] + go n = bigNatIndex bn (n -# 1#) : go (n -# 1#) + + +-- | Convert two Word# (most-significant first) into a BigNat +bigNatFromWord2# :: Word# -> Word# -> BigNat +bigNatFromWord2# 0## 0## = bigNatZero void# +bigNatFromWord2# 0## n = bigNatFromWord# n +bigNatFromWord2# w1 w2 = wordArrayFromWord2# w1 w2 + +-- | Convert a BigNat into a Word# +bigNatToWord# :: BigNat -> Word# +bigNatToWord# a + | bigNatIsZero a = 0## + | True = bigNatIndex# a 0# + +-- | Convert a BigNat into a Word# if it fits +bigNatToWordMaybe# :: BigNat -> (# Word# | () #) +bigNatToWordMaybe# a + | bigNatIsZero a = (# 0## | #) + | isTrue# (bigNatSize# a ># 1#) = (# | () #) + | True = (# bigNatIndex# a 0# | #) + +-- | Convert a BigNat into a Word +bigNatToWord :: BigNat -> Word +bigNatToWord bn = W# (bigNatToWord# bn) + +-- | Convert a BigNat into a Int# +bigNatToInt# :: BigNat -> Int# +bigNatToInt# a + | bigNatIsZero a = 0# + | True = indexIntArray# a 0# + +-- | Convert a BigNat into a Int +bigNatToInt :: BigNat -> Int +bigNatToInt bn = I# (bigNatToInt# bn) + +#if WORD_SIZE_IN_BITS == 32 + +-- | Convert a Word64# into a BigNat on 32-bit architectures +bigNatFromWord64# :: Word64# -> BigNat +bigNatFromWord64# w64 = bigNatFromWord2# wh# wl# + where + wh# = word64ToWord# (uncheckedShiftRL64# w64 32#) + wl# = word64ToWord# w64 + +-- | Convert a BigNat into a Word64# on 32-bit architectures +bigNatToWord64# :: BigNat -> Word64# +bigNatToWord64# b + | bigNatIsZero b = wordToWord64# 0## + | wl <- wordToWord64# (bigNatToWord# b) + = if isTrue# (bigNatSize# b ># 1#) + then + let wh = wordToWord64# (bigNatIndex# b 1#) + in uncheckedShiftL64# wh 32# `or64#` wl + else wl + +#endif + +-- | Encode (# BigNat mantissa, Int# exponent #) into a Double# +bigNatEncodeDouble# :: BigNat -> Int# -> Double# +bigNatEncodeDouble# a e + | bigNatIsZero a + = word2Double# 0## -- FIXME: isn't it NaN on 0# exponent? + + | True + = inline bignat_encode_double a e + +------------------------------------------------- +-- Predicates +------------------------------------------------- + +-- | Test if a BigNat is greater than a Word +bigNatGtWord# :: BigNat -> Word# -> Bool# +bigNatGtWord# bn w = + notB# (bigNatIsZero# bn) + &&# ( bigNatSize# bn ># 1# + ||# bigNatIndex# bn 0# `gtWord#` w + ) + +-- | Test if a BigNat is equal to a Word +bigNatEqWord# :: BigNat -> Word# -> Bool# +bigNatEqWord# bn w + | 0## <- w + = bigNatIsZero# bn + + | isTrue# (bigNatSize# bn ==# 1#) + = bigNatIndex# bn 0# `eqWord#` w + + | True + = 0# + +-- | Test if a BigNat is greater than a Word +bigNatGtWord :: BigNat -> Word -> Bool +bigNatGtWord bn (W# w) = isTrue# (bigNatGtWord# bn w) + +-- | Test if a BigNat is lower than or equal to a Word +bigNatLeWord# :: BigNat -> Word# -> Bool# +bigNatLeWord# bn w = notB# (bigNatGtWord# bn w) + +-- | Test if a BigNat is lower than or equal to a Word +bigNatLeWord :: BigNat -> Word -> Bool +bigNatLeWord bn (W# w) = isTrue# (bigNatLeWord# bn w) + +-- | Equality test for BigNat +bigNatEq# :: BigNat -> BigNat -> Bool# +bigNatEq# wa wb + | isTrue# (wordArraySize# wa /=# wordArraySize# wb) = 0# + | isTrue# (wordArraySize# wa ==# 0#) = 1# + | True = inline bignat_compare wa wb ==# 0# + +-- | Equality test for BigNat +bigNatEq :: BigNat -> BigNat -> Bool +bigNatEq a b = isTrue# (bigNatEq# a b) + +-- | Inequality test for BigNat +bigNatNe# :: BigNat -> BigNat -> Bool# +bigNatNe# a b = notB# (bigNatEq# a b) + +-- | Equality test for BigNat +bigNatNe :: BigNat -> BigNat -> Bool +bigNatNe a b = isTrue# (bigNatNe# a b) + +-- | Compare a BigNat and a Word# +bigNatCompareWord# :: BigNat -> Word# -> Ordering +bigNatCompareWord# a b + | bigNatIsZero a = cmpW# 0## b + | isTrue# (wordArraySize# a ># 1#) = GT + | True + = cmpW# (indexWordArray# a 1#) b + +-- | Compare a BigNat and a Word +bigNatCompareWord :: BigNat -> Word -> Ordering +bigNatCompareWord a (W# b) = bigNatCompareWord# a b + +-- | Compare two BigNat +bigNatCompare :: BigNat -> BigNat -> Ordering +bigNatCompare a b = + let + szA = wordArraySize# a + szB = wordArraySize# b + in if + | isTrue# (szA ># szB) -> GT + | isTrue# (szA <# szB) -> LT + | isTrue# (szA ==# 0#) -> EQ + | True -> compareInt# (inline bignat_compare a b) 0# + + +-- | Predicate: a < b +bigNatLt :: BigNat -> BigNat -> Bool +bigNatLt a b = bigNatCompare a b == LT + +------------------------------------------------- +-- Addition +------------------------------------------------- + +-- | Add a bigNat and a Word# +bigNatAddWord# :: BigNat -> Word# -> BigNat +bigNatAddWord# a b + | 0## <- b + = a + + | bigNatIsZero a + = bigNatFromWord# b + + | True + = withNewWordArrayTrimed# (wordArraySize# a +# 1#) \mwa s -> + inline bignat_add_word mwa a b s + +-- | Add a bigNat and a Word +bigNatAddWord :: BigNat -> Word -> BigNat +bigNatAddWord a (W# b) = bigNatAddWord# a b + +-- | Add two bigNats +bigNatAdd :: BigNat -> BigNat -> BigNat +bigNatAdd a b + | bigNatIsZero a = b + | bigNatIsZero b = a + | True = + let + !szA = wordArraySize# a + !szB = wordArraySize# b + !szMax = maxI# szA szB + !sz = szMax +# 1# -- for the potential carry + in withNewWordArrayTrimed# sz \mwa s -> + inline bignat_add mwa a b s + +------------------------------------------------- +-- Multiplication +------------------------------------------------- + +-- | Multiply a BigNat by a Word# +bigNatMulWord# :: BigNat -> Word# -> BigNat +bigNatMulWord# a w + | 0## <- w = bigNatZero void# + | 1## <- w = a + | bigNatIsZero a = bigNatZero void# + | bigNatIsOne a = bigNatFromWord# w + | isTrue# (bigNatSize# a ==# 1#) + = case timesWord2# (bigNatIndex# a 0#) w of + (# h, l #) -> bigNatFromWord2# h l + | True = withNewWordArrayTrimed# (bigNatSize# a +# 1#) \mwa s -> + inline bignat_mul_word mwa a w s + +-- | Multiply a BigNAt by a Word +bigNatMulWord :: BigNat -> Word -> BigNat +bigNatMulWord a (W# w) = bigNatMulWord# a w + +-- | Square a BigNat +bigNatSqr :: BigNat -> BigNat +bigNatSqr a = bigNatMul a a + -- This can be replaced by a backend primitive in the future (e.g. to use + -- GMP's mpn_sqr) + +-- | Multiplication (classical algorithm) +bigNatMul :: BigNat -> BigNat -> BigNat +bigNatMul a b + | bigNatSize b > bigNatSize a = bigNatMul b a -- optimize loops + | bigNatIsZero a = a + | bigNatIsZero b = b + | bigNatIsOne a = b + | bigNatIsOne b = a + | True = + let + !szA = wordArraySize# a + !szB = wordArraySize# b + !sz = szA +# szB + in withNewWordArrayTrimed# sz \mwa s-> + inline bignat_mul mwa a b s + + +------------------------------------------------- +-- Subtraction +------------------------------------------------- + +-- | Subtract a Word# from a BigNat +-- +-- The BigNat must be bigger than the Word#. +bigNatSubWordUnsafe# :: BigNat -> Word# -> BigNat +bigNatSubWordUnsafe# x y + | 0## <- y = x + | True = withNewWordArrayTrimed# sz \mwa -> go mwa y 0# + where + !sz = wordArraySize# x + + go mwa carry i s + | isTrue# (i >=# sz) + = s + + | 0## <- carry + = mwaArrayCopy# mwa i x i (sz -# i) s + + | True + = case subWordC# (indexWordArray# x i) carry of + (# l, c #) -> case mwaWrite# mwa i l s of + s1 -> go mwa (int2Word# c) (i +# 1#) s1 + +-- | Subtract a Word# from a BigNat +-- +-- The BigNat must be bigger than the Word#. +bigNatSubWordUnsafe :: BigNat -> Word -> BigNat +bigNatSubWordUnsafe x (W# y) = bigNatSubWordUnsafe# x y + +-- | Subtract a Word# from a BigNat +bigNatSubWord# :: BigNat -> Word# -> (# () | BigNat #) +bigNatSubWord# a b + | 0## <- b = (# | a #) + | bigNatIsZero a = (# () | #) + | True + = withNewWordArrayTrimedMaybe# (bigNatSize# a) \mwa s -> + inline bignat_sub_word mwa a b s + + +-- | Subtract two BigNat (don't check if a >= b) +bigNatSubUnsafe :: BigNat -> BigNat -> BigNat +bigNatSubUnsafe a b + | bigNatIsZero b = a + | True = + let szA = wordArraySize# a + in withNewWordArrayTrimed# szA \mwa s-> + case inline bignat_sub mwa a b s of + (# s', 0# #) -> s' + (# s', _ #) -> case underflow of _ -> s' + +-- | Subtract two BigNat +bigNatSub :: BigNat -> BigNat -> (# () | BigNat #) +bigNatSub a b + | bigNatIsZero b = (# | a #) + | isTrue# (bigNatSize# a <# bigNatSize# b) + = (# () | #) + + | True + = withNewWordArrayTrimedMaybe# (bigNatSize# a) \mwa s -> + inline bignat_sub mwa a b s + + +------------------------------------------------- +-- Division +------------------------------------------------- + +-- | Divide a BigNat by a Word, return the quotient +-- +-- Require: +-- b /= 0 +bigNatQuotWord# :: BigNat -> Word# -> BigNat +bigNatQuotWord# a b + | 1## <- b = a + | 0## <- b = case divByZero of _ -> bigNatZero void# + | True = + let + sz = wordArraySize# a + in withNewWordArrayTrimed# sz \mwq s -> + inline bignat_quot_word mwq a b s + +-- | Divide a BigNat by a Word, return the quotient +-- +-- Require: +-- b /= 0 +bigNatQuotWord :: BigNat -> Word -> BigNat +bigNatQuotWord a (W# b) = bigNatQuotWord# a b + +-- | Divide a BigNat by a Word, return the remainder +-- +-- Require: +-- b /= 0 +bigNatRemWord# :: BigNat -> Word# -> Word# +bigNatRemWord# a b + | 0## <- b = 1## `remWord#` 0## + | 1## <- b = 0## + | bigNatIsZero a = 0## + | True = inline bignat_rem_word a b + +-- | Divide a BigNat by a Word, return the remainder +-- +-- Require: +-- b /= 0 +bigNatRemWord :: BigNat -> Word -> Word +bigNatRemWord a (W# b) = W# (bigNatRemWord# a b) + +-- | QuotRem a BigNat by a Word +-- +-- Require: +-- b /= 0 +bigNatQuotRemWord# :: BigNat -> Word# -> (# BigNat, Word# #) +bigNatQuotRemWord# a b + | 0## <- b = case divByZero of _ -> (# bigNatZero void#, 0## #) + | 1## <- b = (# a, 0## #) + | isTrue# (bigNatSize# a ==# 1#) + , a0 <- indexWordArray# a 0# + = case compareWord# a0 b of + LT -> (# bigNatZero void#, a0 #) + EQ -> (# bigNatOne void#, 0## #) + GT -> case quotRemWord# a0 b of + (# q, r #) -> (# bigNatFromWord# q, r #) + | True = + let + sz = wordArraySize# a + io s = + case newWordArray# sz s of { (# s1, mwq #) -> + case inline bignat_quotrem_word mwq a b s1 of { (# s2, r #) -> + case mwaTrimZeroes# mwq s2 of { s3 -> + case unsafeFreezeByteArray# mwq s3 of { (# s4, wq #) -> + (# s4, (# wq, r #) #) + }}}} + in case runRW# io of + (# _, (# wq,r #) #) -> (# wq, r #) + + +-- | BigNat division returning (quotient,remainder) +bigNatQuotRem# :: BigNat -> BigNat -> (# BigNat,BigNat #) +bigNatQuotRem# a b + | bigNatIsZero b = case divByZero of _ -> (# bigNatZero void#, bigNatZero void# #) + | bigNatIsZero a = (# bigNatZero void#, bigNatZero void# #) + | bigNatIsOne b = (# a , bigNatZero void# #) + | LT <- cmp = (# bigNatZero void#, a #) + | EQ <- cmp = (# bigNatOne void#, bigNatZero void# #) + | isTrue# (szB ==# 1#) = case bigNatQuotRemWord# a (bigNatIndex# b 0#) of + (# q, r #) -> (# q, bigNatFromWord# r #) + + | True = withNewWordArray2Trimed# szQ szR \mwq mwr s -> + inline bignat_quotrem mwq mwr a b s + where + cmp = bigNatCompare a b + szA = wordArraySize# a + szB = wordArraySize# b + szQ = 1# +# szA -# szB + szR = szB + + +-- | BigNat division returning quotient +bigNatQuot :: BigNat -> BigNat -> BigNat +bigNatQuot a b + | bigNatIsZero b = case divByZero of _ -> bigNatZero void# + | bigNatIsZero a = bigNatZero void# + | bigNatIsOne b = a + | LT <- cmp = bigNatZero void# + | EQ <- cmp = bigNatOne void# + | isTrue# (szB ==# 1#) = bigNatQuotWord# a (bigNatIndex# b 0#) + | True = withNewWordArrayTrimed# szQ \mwq s -> + inline bignat_quot mwq a b s + where + cmp = bigNatCompare a b + szA = wordArraySize# a + szB = wordArraySize# b + szQ = 1# +# szA -# szB + +-- | BigNat division returning remainder +bigNatRem :: BigNat -> BigNat -> BigNat +bigNatRem a b + | bigNatIsZero b = case divByZero of _ -> bigNatZero void# + | bigNatIsZero a = bigNatZero void# + | bigNatIsOne b = bigNatZero void# + | LT <- cmp = a + | EQ <- cmp = bigNatZero void# + | isTrue# (szB ==# 1#) = case bigNatRemWord# a (bigNatIndex# b 0#) of + r -> bigNatFromWord# r + | True = withNewWordArrayTrimed# szR \mwr s -> + inline bignat_rem mwr a b s + where + cmp = bigNatCompare a b + szB = wordArraySize# b + szR = szB + +------------------------------------------------- +-- GCD / LCM +------------------------------------------------- + +-- Word#/Int# GCDs shouldn't be here in BigNat. However GMP provides a very fast +-- implementation so we keep this here at least until we get a native Haskell +-- implementation as fast as GMP's one. Note that these functions are used in +-- `base` (e.g. in GHC.Real) + +-- | Greatest common divisor between two Word# +gcdWord# :: Word# -> Word# -> Word# +gcdWord# = bignat_gcd_word_word + +-- | Greatest common divisor between two Word +gcdWord :: Word -> Word -> Word +gcdWord (W# x) (W# y) = W# (gcdWord# x y) + +-- | Greatest common divisor between two Int# +-- +-- __Warning__: result may become negative if (at least) one argument +-- is 'minBound' +gcdInt# :: Int# -> Int# -> Int# +gcdInt# x y = word2Int# (gcdWord# (wordFromAbsInt# x) (wordFromAbsInt# y)) + +-- | Greatest common divisor between two Int +-- +-- __Warning__: result may become negative if (at least) one argument +-- is 'minBound' +gcdInt :: Int -> Int -> Int +gcdInt (I# x) (I# y) = I# (gcdInt# x y) + +-- | Greatest common divisor +bigNatGcd :: BigNat -> BigNat -> BigNat +bigNatGcd a b + | bigNatIsZero a = b + | bigNatIsZero b = a + | bigNatIsOne a = a + | bigNatIsOne b = b + | True + = case (# bigNatSize# a, bigNatSize# b #) of + (# 1#, 1# #) -> bigNatFromWord# (gcdWord# (bigNatIndex# a 0#) + (bigNatIndex# b 0#)) + (# 1#, _ #) -> bigNatFromWord# (bigNatGcdWord# b (bigNatIndex# a 0#)) + (# _ , 1# #) -> bigNatFromWord# (bigNatGcdWord# a (bigNatIndex# b 0#)) + _ -> + let + go wx wy = -- wx > wy + withNewWordArrayTrimed# (wordArraySize# wy) \mwr s -> + bignat_gcd mwr wx wy s + in case bigNatCompare a b of + EQ -> a + LT -> go b a + GT -> go a b + +-- | Greatest common divisor +bigNatGcdWord# :: BigNat -> Word# -> Word# +bigNatGcdWord# a b + | bigNatIsZero a = 0## + | 0## <- b = 0## + | bigNatIsOne a = 1## + | 1## <- b = 1## + | True = case bigNatCompareWord# a b of + EQ -> b + _ -> bignat_gcd_word a b + +-- | Least common multiple +bigNatLcm :: BigNat -> BigNat -> BigNat +bigNatLcm a b + | bigNatIsZero a = bigNatZero void# + | bigNatIsZero b = bigNatZero void# + | bigNatIsOne a = b + | bigNatIsOne b = a + | True + = case (# bigNatSize# a, bigNatSize# b #) of + (# 1#, 1# #) -> bigNatLcmWordWord# (bigNatIndex# a 0#) (bigNatIndex# b 0#) + (# 1#, _ #) -> bigNatLcmWord# b (bigNatIndex# a 0#) + (# _ , 1# #) -> bigNatLcmWord# a (bigNatIndex# b 0#) + _ -> (a `bigNatQuot` (a `bigNatGcd` b)) `bigNatMul` b + -- TODO: use extended GCD to get a's factor directly + +-- | Least common multiple with a Word# +bigNatLcmWord# :: BigNat -> Word# -> BigNat +bigNatLcmWord# a b + | bigNatIsZero a = bigNatZero void# + | 0## <- b = bigNatZero void# + | bigNatIsOne a = bigNatFromWord# b + | 1## <- b = a + | 1# <- bigNatSize# a = bigNatLcmWordWord# (bigNatIndex# a 0#) b + | True + = (a `bigNatQuotWord#` (a `bigNatGcdWord#` b)) `bigNatMulWord#` b + -- TODO: use extended GCD to get a's factor directly + +-- | Least common multiple between two Word# +bigNatLcmWordWord# :: Word# -> Word# -> BigNat +bigNatLcmWordWord# a b + | 0## <- a = bigNatZero void# + | 0## <- b = bigNatZero void# + | 1## <- a = bigNatFromWord# b + | 1## <- b = bigNatFromWord# a + | True = case (a `quotWord#` (a `gcdWord#` b)) `timesWord2#` b of + -- TODO: use extended GCD to get a's factor directly + (# h, l #) -> bigNatFromWord2# h l + + +------------------------------------------------- +-- Bitwise operations +------------------------------------------------- + +-- | Bitwise OR +bigNatOr :: BigNat -> BigNat -> BigNat +bigNatOr a b + | bigNatIsZero a = b + | bigNatIsZero b = a + | True = withNewWordArray# sz \mwa s -> + inline bignat_or mwa a b s + where + !szA = wordArraySize# a + !szB = wordArraySize# b + !sz = maxI# szA szB + +-- | Bitwise OR with Word# +bigNatOrWord# :: BigNat -> Word# -> BigNat +bigNatOrWord# a b + | bigNatIsZero a = bigNatFromWord# b + | 0## <- b = a + | True = + let sz = wordArraySize# a + in withNewWordArray# sz \mwa s -> + case mwaArrayCopy# mwa 1# a 1# (sz -# 1#) s of + s' -> mwaWrite# mwa 0# (indexWordArray# a 0# `or#` b) s' + +-- | Bitwise AND +bigNatAnd :: BigNat -> BigNat -> BigNat +bigNatAnd a b + | bigNatIsZero a = a + | bigNatIsZero b = b + | True = withNewWordArrayTrimed# sz \mwa s -> + inline bignat_and mwa a b s + where + !szA = wordArraySize# a + !szB = wordArraySize# b + !sz = minI# szA szB + +-- | Bitwise ANDNOT +bigNatAndNot :: BigNat -> BigNat -> BigNat +bigNatAndNot a b + | bigNatIsZero a = a + | bigNatIsZero b = a + | True = withNewWordArrayTrimed# szA \mwa s -> + inline bignat_and_not mwa a b s + where + !szA = wordArraySize# a + +-- | Bitwise AND with Word# +bigNatAndWord# :: BigNat -> Word# -> BigNat +bigNatAndWord# a b + | bigNatIsZero a = a + | True = bigNatFromWord# (indexWordArray# a 0# `and#` b) + +-- | Bitwise ANDNOT with Word# +bigNatAndNotWord# :: BigNat -> Word# -> BigNat +bigNatAndNotWord# a b + | bigNatIsZero a = a + | szA <- bigNatSize# a + = withNewWordArray# szA \mwa s -> + -- duplicate higher limbs + case mwaArrayCopy# mwa 1# a 1# (szA -# 1#) s of + s' -> writeWordArray# mwa 0# + (indexWordArray# a 0# `and#` not# b) s' + +-- | Bitwise AND with Int# +bigNatAndInt# :: BigNat -> Int# -> BigNat +bigNatAndInt# a b + | bigNatIsZero a = a + | isTrue# (b >=# 0#) = bigNatAndWord# a (int2Word# b) + | szA <- bigNatSize# a + = withNewWordArray# szA \mwa s -> + -- duplicate higher limbs (because of sign-extension of b) + case mwaArrayCopy# mwa 1# a 1# (szA -# 1#) s of + s' -> writeWordArray# mwa 0# + (indexWordArray# a 0# `and#` int2Word# b) s' + + +-- | Bitwise XOR +bigNatXor :: BigNat -> BigNat -> BigNat +bigNatXor a b + | bigNatIsZero a = b + | bigNatIsZero b = a + | True = withNewWordArrayTrimed# sz \mwa s -> + inline bignat_xor mwa a b s + where + !szA = wordArraySize# a + !szB = wordArraySize# b + !sz = maxI# szA szB + +-- | Bitwise XOR with Word# +bigNatXorWord# :: BigNat -> Word# -> BigNat +bigNatXorWord# a b + | bigNatIsZero a = bigNatFromWord# b + | 0## <- b = a + | True = + let + sz = wordArraySize# a + in withNewWordArray# sz \mwa s -> + case mwaArrayCopy# mwa 1# a 1# (sz -# 1#) s of + s' -> mwaWrite# mwa 0# (indexWordArray# a 0# `xor#` b) s' + +-- | PopCount for BigNat +bigNatPopCount :: BigNat -> Word +bigNatPopCount a = W# (bigNatPopCount# a) + +-- | PopCount for BigNat +bigNatPopCount# :: BigNat -> Word# +bigNatPopCount# a + | bigNatIsZero a = 0## + | True = inline bignat_popcount a + +-- | Bit shift right +bigNatShiftR# :: BigNat -> Word# -> BigNat +bigNatShiftR# a n + | 0## <- n + = a + + | isTrue# (wordArraySize# a ==# 0#) + = a + + | nw <- word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + , isTrue# (nw >=# wordArraySize# a) + = bigNatZero void# + + | True + = let + !szA = wordArraySize# a + !nw = word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + !sz = szA -# nw + in withNewWordArrayTrimed# sz \mwa s -> + inline bignat_shiftr mwa a n s + +-- | Bit shift right (two's complement) +bigNatShiftRNeg# :: BigNat -> Word# -> BigNat +bigNatShiftRNeg# a n + | 0## <- n + = a + + | isTrue# (wordArraySize# a ==# 0#) + = a + + | nw <- word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + , isTrue# (nw >=# wordArraySize# a) + = bigNatZero void# + + | True + = let + !szA = wordArraySize# a + !nw = (word2Int# n -# 1#) `uncheckedIShiftRL#` WORD_SIZE_BITS_SHIFT# + !sz = szA -# nw + in withNewWordArrayTrimed# sz \mwa s -> + inline bignat_shiftr_neg mwa a n s + + +-- | Bit shift right +bigNatShiftR :: BigNat -> Word -> BigNat +bigNatShiftR a (W# n) = bigNatShiftR# a n + +-- | Bit shift left +bigNatShiftL :: BigNat -> Word -> BigNat +bigNatShiftL a (W# n) = bigNatShiftL# a n + +-- | Bit shift left +bigNatShiftL# :: BigNat -> Word# -> BigNat +bigNatShiftL# a n + | 0## <- n + = a + + | isTrue# (wordArraySize# a ==# 0#) + = a + + | True + = let + !szA = wordArraySize# a + !nw = word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + !nb = word2Int# (n `and#` WORD_SIZE_BITS_MASK##) + !sz = szA +# nw +# (nb /=# 0#) + + in withNewWordArrayTrimed# sz \mwa s -> + inline bignat_shiftl mwa a n s + + +-- | BigNat bit test +bigNatTestBit# :: BigNat -> Word# -> Bool# +bigNatTestBit# a n = + let + !sz = wordArraySize# a + !nw = word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + !nb = n `and#` WORD_SIZE_BITS_MASK## + in if + | isTrue# (nw >=# sz) -> 0# + | True -> testBitW# (indexWordArray# a nw) nb + +-- | BigNat bit test +bigNatTestBit :: BigNat -> Word -> Bool +bigNatTestBit a (W# n) = isTrue# (bigNatTestBit# a n) + + +-- | Return a BigNat whose bit `i` is the only one set. +-- +-- Specialized version of `bigNatShiftL (bigNatFromWord# 1##)` +-- +bigNatBit# :: Word# -> BigNat +bigNatBit# i + | 0## <- i = bigNatOne void# + | True = + let + !nw = word2Int# (i `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + !nb = word2Int# (i `and#` WORD_SIZE_BITS_MASK##) + !sz = nw +# 1# + !v = 1## `uncheckedShiftL#` nb + in withNewWordArray# sz \mwa s -> + -- clear the array + case mwaFill# mwa 0## 0## (int2Word# sz) s of + -- set the bit in the most-significant word + s2 -> mwaWrite# mwa (sz -# 1#) v s2 + +-- | Return a BigNat whose bit `i` is the only one set. +-- +-- Specialized version of `bigNatShiftL (bigNatFromWord# 1##)` +-- +bigNatBit :: Word -> BigNat +bigNatBit (W# i) = bigNatBit# i + +-- | BigNat clear bit +bigNatClearBit# :: BigNat -> Word# -> BigNat +bigNatClearBit# a n + -- check the range validity and the current bit value + | isTrue# (bigNatTestBit# a n ==# 0#) = a + | True + = let + !sz = wordArraySize# a + !nw = word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + !nb = word2Int# (n `and#` WORD_SIZE_BITS_MASK##) + !nv = bigNatIndex# a nw `xor#` bitW# nb + in if + | isTrue# (sz ==# 1#) + -> bigNatFromWord# nv + + -- special case, operating on most-significant Word + | 0## <- nv + , isTrue# (nw +# 1# ==# sz) + -> case sz -# (waClzAt a (sz -# 2#) +# 1#) of + 0# -> bigNatZero void# + nsz -> withNewWordArray# nsz \mwa s -> + mwaArrayCopy# mwa 0# a 0# nsz s + + | True -> + withNewWordArray# sz \mwa s -> + case mwaArrayCopy# mwa 0# a 0# sz s of + s' -> writeWordArray# mwa nw nv s' + +-- | BigNat set bit +bigNatSetBit# :: BigNat -> Word# -> BigNat +bigNatSetBit# a n + -- check the current bit value + | isTrue# (bigNatTestBit# a n) = a + | True + = let + !sz = wordArraySize# a + !nw = word2Int# (n `uncheckedShiftRL#` WORD_SIZE_BITS_SHIFT#) + !nb = word2Int# (n `and#` WORD_SIZE_BITS_MASK##) + d = nw +# 1# -# sz + in if + -- result BigNat will have more limbs + | isTrue# (d ># 0#) + -> withNewWordArray# (nw +# 1#) \mwa s -> + case mwaArrayCopy# mwa 0# a 0# sz s of + s' -> case mwaFill# mwa 0## (int2Word# sz) (int2Word# (d -# 1#)) s' of + s'' -> writeWordArray# mwa nw (bitW# nb) s'' + + | nv <- bigNatIndex# a nw `or#` bitW# nb + -> withNewWordArray# sz \mwa s -> + case mwaArrayCopy# mwa 0# a 0# sz s of + s' -> writeWordArray# mwa nw nv s' + +-- | Reverse the given bit +bigNatComplementBit# :: BigNat -> Word# -> BigNat +bigNatComplementBit# bn i + | isTrue# (bigNatTestBit# bn i) = bigNatClearBit# bn i + | True = bigNatSetBit# bn i + +------------------------------------------------- +-- Log operations +------------------------------------------------- + +-- | Base 2 logarithm +bigNatLog2# :: BigNat -> Word# +bigNatLog2# a + | bigNatIsZero a = 0## + | True = + let i = int2Word# (bigNatSize# a) `minusWord#` 1## + in wordLog2# (bigNatIndex# a (word2Int# i)) + `plusWord#` (i `uncheckedShiftL#` WORD_SIZE_BITS_SHIFT#) + +-- | Base 2 logarithm +bigNatLog2 :: BigNat -> Word +bigNatLog2 a = W# (bigNatLog2# a) + +-- | Logarithm for an arbitrary base +bigNatLogBase# :: BigNat -> BigNat -> Word# +bigNatLogBase# base a + | bigNatIsZero base || bigNatIsOne base + = case unexpectedValue of _ -> 0## + + | 1# <- bigNatSize# base + , 2## <- bigNatIndex# base 0# + = bigNatLog2# a + + -- TODO: optimize log base power of 2 (256, etc.) + + | True + = case go base of (# _, e' #) -> e' + where + go pw = if a `bigNatLt` pw + then (# a, 0## #) + else case go (bigNatSqr pw) of + (# q, e #) -> if q `bigNatLt` pw + then (# q, 2## `timesWord#` e #) + else (# q `bigNatQuot` pw + , (2## `timesWord#` e) `plusWord#` 1## #) + +-- | Logarithm for an arbitrary base +bigNatLogBase :: BigNat -> BigNat -> Word +bigNatLogBase base a = W# (bigNatLogBase# base a) + +-- | Logarithm for an arbitrary base +bigNatLogBaseWord# :: Word# -> BigNat -> Word# +bigNatLogBaseWord# base a + | 0## <- base = case unexpectedValue of _ -> 0## + | 1## <- base = case unexpectedValue of _ -> 0## + | 2## <- base = bigNatLog2# a + -- TODO: optimize log base power of 2 (256, etc.) + | True = bigNatLogBase# (bigNatFromWord# base) a + +-- | Logarithm for an arbitrary base +bigNatLogBaseWord :: Word -> BigNat -> Word +bigNatLogBaseWord (W# base) a = W# (bigNatLogBaseWord# base a) + +------------------------------------------------- +-- Various +------------------------------------------------- + +-- | Compute the number of digits of the BigNat in the given base. +-- +-- `base` must be > 1 +bigNatSizeInBase# :: Word# -> BigNat -> Word# +bigNatSizeInBase# base a + | isTrue# (base `leWord#` 1##) + = case unexpectedValue of _ -> 0## + + | bigNatIsZero a + = 0## + + | True + = bigNatLogBaseWord# base a `plusWord#` 1## + +-- | Compute the number of digits of the BigNat in the given base. +-- +-- `base` must be > 1 +bigNatSizeInBase :: Word -> BigNat -> Word +bigNatSizeInBase (W# w) a = W# (bigNatSizeInBase# w a) + +------------------------------------------------- +-- PowMod +------------------------------------------------- + +-- Word# powMod shouldn't be here in BigNat. However GMP provides a very fast +-- implementation so we keep this here at least until we get a native Haskell +-- implementation as fast as GMP's one. + +powModWord# :: Word# -> Word# -> Word# -> Word# +powModWord# = bignat_powmod_words + + +-- | \"@'bigNatPowModWord#' /b/ /e/ /m/@\" computes base @/b/@ raised to +-- exponent @/e/@ modulo @/m/@. +bigNatPowModWord# :: BigNat -> BigNat -> Word# -> Word# +bigNatPowModWord# !_ !_ 0## = case divByZero of _ -> 0## +bigNatPowModWord# _ _ 1## = 0## +bigNatPowModWord# b e m + | bigNatIsZero e = 1## + | bigNatIsZero b = 0## + | bigNatIsOne b = 1## + | True = bignat_powmod_word b e m + +-- | \"@'bigNatPowMod' /b/ /e/ /m/@\" computes base @/b/@ raised to +-- exponent @/e/@ modulo @/m/@. +bigNatPowMod :: BigNat -> BigNat -> BigNat -> BigNat +bigNatPowMod !b !e !m + | (# m' | #) <- bigNatToWordMaybe# m + = bigNatFromWord# (bigNatPowModWord# b e m') + | bigNatIsZero m = case divByZero of _ -> bigNatZero void# + | bigNatIsOne m = bigNatFromWord# 0## + | bigNatIsZero e = bigNatFromWord# 1## + | bigNatIsZero b = bigNatFromWord# 0## + | bigNatIsOne b = bigNatFromWord# 1## + | True = withNewWordArrayTrimed# (bigNatSize# m) \mwa s -> + inline bignat_powmod mwa b e m s + +-- | Return count of trailing zero bits +-- +-- Return 0 for zero BigNat +bigNatCtz# :: BigNat -> Word# +bigNatCtz# a + | bigNatIsZero a = 0## + | True = go 0# 0## + where + go i c = case indexWordArray# a i of + 0## -> go (i +# 1#) (c `plusWord#` WORD_SIZE_IN_BITS##) + w -> ctz# w `plusWord#` c + +-- | Return count of trailing zero bits +-- +-- Return 0 for zero BigNat +bigNatCtz :: BigNat -> Word +bigNatCtz a = W# (bigNatCtz# a) + + +-- | Return count of trailing zero words +-- +-- Return 0 for zero BigNat +bigNatCtzWord# :: BigNat -> Word# +bigNatCtzWord# a + | bigNatIsZero a = 0## + | True = go 0# 0## + where + go i c = case indexWordArray# a i of + 0## -> go (i +# 1#) (c `plusWord#` 1##) + _ -> c + +-- | Return count of trailing zero words +-- +-- Return 0 for zero BigNat +bigNatCtzWord :: BigNat -> Word +bigNatCtzWord a = W# (bigNatCtzWord# a) + +------------------------------------------------- +-- Export to memory +------------------------------------------------- + +-- | Write a BigNat in base-256 little-endian representation and return the +-- number of bytes written. +-- +-- Use \"@'bigNatSizeInBase' 256# /i/@\" to compute the exact number of bytes +-- written in advance. In case of @/i/ == 0@, the function will write and report +-- zero bytes written. +bigNatToAddrLE# :: BigNat -> Addr# -> State# s -> (# State# s, Word# #) +bigNatToAddrLE# a addr s0 + | isTrue# (sz ==# 0#) = (# s0, 0## #) + | True = case writeMSB s0 of + (# s1, k #) -> case go 0# s1 of + s2 -> (# s2, k `plusWord#` (int2Word# li `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT#) #) + where + !sz = wordArraySize# a + !li = sz -# 1# + + writeMSB = wordToAddrLE# (indexWordArray# a li) + (addr `plusAddr#` (li `uncheckedIShiftL#` WORD_SIZE_BYTES_SHIFT#)) + + go i s + | isTrue# (i <# li) + , off <- i `uncheckedIShiftL#` WORD_SIZE_BYTES_SHIFT# + , w <- indexWordArray# a i + = case wordWriteAddrLE# w (addr `plusAddr#` off) s of + s -> go (i +# 1#) s + + | True + = s + +-- | Write a BigNat in base-256 big-endian representation and return the +-- number of bytes written. +-- +-- Use \"@'bigNatSizeInBase' 256# /i/@\" to compute the exact number of bytes +-- written in advance. In case of @/i/ == 0@, the function will write and report +-- zero bytes written. +bigNatToAddrBE# :: BigNat -> Addr# -> State# s -> (# State# s, Word# #) +bigNatToAddrBE# a addr s0 + | isTrue# (sz ==# 0#) = (# s0, 0## #) + | msw <- indexWordArray# a (sz -# 1#) + = case wordToAddrBE# msw addr s0 of + (# s1, k #) -> case go (sz -# 1#) (addr `plusAddr#` word2Int# k) s1 of + s2 -> (# s2, k `plusWord#` (int2Word# (sz -# 1#) `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT#) #) + where + sz = wordArraySize# a + + go i adr s + | 0# <- i + = s + + | w <- indexWordArray# a (i -# 1#) + = case wordWriteAddrBE# w adr s of + s' -> go (i -# 1#) + (adr `plusAddr#` WORD_SIZE_IN_BYTES# ) s' + + +-- | Write a BigNat in base-256 representation and return the +-- number of bytes written. +-- +-- The endianness is selected with the Bool# parameter: most significant +-- byte first (big-endian) if @1#@ or least significant byte first +-- (little-endian) if @0#@. +-- +-- Use \"@'bigNatSizeInBase' 256# /i/@\" to compute the exact number of bytes +-- written in advance. In case of @/i/ == 0@, the function will write and report +-- zero bytes written. +bigNatToAddr# :: BigNat -> Addr# -> Bool# -> State# s -> (# State# s, Word# #) +bigNatToAddr# a addr 0# s = bigNatToAddrLE# a addr s +bigNatToAddr# a addr _ s = bigNatToAddrBE# a addr s + +-- | Write a BigNat in base-256 representation and return the +-- number of bytes written. +-- +-- The endianness is selected with the Bool# parameter: most significant +-- byte first (big-endian) if @1#@ or least significant byte first +-- (little-endian) if @0#@. +-- +-- Use \"@'bigNatSizeInBase' 256# /i/@\" to compute the exact number of bytes +-- written in advance. In case of @/i/ == 0@, the function will write and report +-- zero bytes written. +bigNatToAddr :: BigNat -> Addr# -> Bool# -> IO Word +bigNatToAddr a addr e = IO \s -> case bigNatToAddr# a addr e s of + (# s', w #) -> (# s', W# w #) + + + +------------------------------------------------- +-- Import from memory +------------------------------------------------- + +-- | Read a BigNat in base-256 little-endian representation from an Addr#. +-- +-- The size is given in bytes. +-- +-- Higher limbs equal to 0 are automatically trimed. +bigNatFromAddrLE# :: Word# -> Addr# -> State# s -> (# State# s, BigNat #) +bigNatFromAddrLE# 0## _ s = (# s, bigNatZero void# #) +bigNatFromAddrLE# sz addr s = + let + !nw = sz `uncheckedShiftRL#` WORD_SIZE_BYTES_SHIFT# + !nb = sz `and#` WORD_SIZE_BYTES_MASK## + + readMSB mwa s + | 0## <- nb + = s + + | off <- word2Int# (nw `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT#) + = case wordFromAddrLE# nb (addr `plusAddr#` off) s of + (# s, w #) -> mwaWrite# mwa (word2Int# nw) w s + + go mwa i s + | isTrue# (i ==# word2Int# nw) + = s + + | off <- i `uncheckedIShiftL#` WORD_SIZE_BYTES_SHIFT# + = case wordFromAddrLE# WORD_SIZE_IN_BYTES## (addr `plusAddr#` off) s of + (# s, w #) -> case mwaWrite# mwa i w s of + s -> go mwa (i +# 1#) s + + in case newWordArray# (word2Int# nw +# (word2Int# nb /=# 0#)) s of + (# s, mwa #) -> case readMSB mwa s of + s -> case go mwa 0# s of + s -> case mwaTrimZeroes# mwa s of + s -> unsafeFreezeByteArray# mwa s + +-- | Read a BigNat in base-256 big-endian representation from an Addr#. +-- +-- The size is given in bytes. +-- +-- Null higher limbs are automatically trimed. +bigNatFromAddrBE# :: Word# -> Addr# -> State# s -> (# State# s, BigNat #) +bigNatFromAddrBE# 0## _ s = (# s, bigNatZero void# #) +bigNatFromAddrBE# sz addr s = + let + !nw = word2Int# (sz `uncheckedShiftRL#` WORD_SIZE_BYTES_SHIFT#) + !nb = sz `and#` WORD_SIZE_BYTES_MASK## + + goMSB mwa s + | 0## <- nb + = s + + | True + = case wordFromAddrBE# nb addr s of + (# s, w #) -> mwaWrite# mwa nw w s + + go mwa i s + | isTrue# (i ==# nw) + = s + + | k <- nw -# 1# -# i + , off <- (k `uncheckedIShiftL#` WORD_SIZE_BYTES_SHIFT#) +# word2Int# nb + = case wordFromAddrBE# WORD_SIZE_IN_BYTES## (addr `plusAddr#` off) s of + (# s, w #) -> case mwaWrite# mwa i w s of + s -> go mwa (i +# 1#) s + + in case newWordArray# (nw +# (word2Int# nb /=# 0#)) s of + (# s, mwa #) -> case goMSB mwa s of + s -> case go mwa 0# s of + s -> case mwaTrimZeroes# mwa s of + s -> unsafeFreezeByteArray# mwa s + +-- | Read a BigNat in base-256 representation from an Addr#. +-- +-- The size is given in bytes. +-- +-- The endianness is selected with the Bool# parameter: most significant +-- byte first (big-endian) if @1#@ or least significant byte first +-- (little-endian) if @0#@. +-- +-- Null higher limbs are automatically trimed. +bigNatFromAddr# :: Word# -> Addr# -> Bool# -> State# s -> (# State# s, BigNat #) +bigNatFromAddr# sz addr 0# s = bigNatFromAddrLE# sz addr s +bigNatFromAddr# sz addr _ s = bigNatFromAddrBE# sz addr s + +------------------------------------------------- +-- Export to ByteArray +------------------------------------------------- + +-- | Write a BigNat in base-256 little-endian representation and return the +-- number of bytes written. +-- +-- Use \"@'bigNatSizeInBase' 256# /i/@\" to compute the exact number of bytes +-- written in advance. In case of @/i/ == 0@, the function will write and report +-- zero bytes written. +bigNatToMutableByteArrayLE# :: BigNat -> MutableByteArray# s -> Word# -> State# s -> (# State# s, Word# #) +bigNatToMutableByteArrayLE# a mba moff s0 + | isTrue# (sz ==# 0#) = (# s0, 0## #) + | True = case writeMSB s0 of + (# s1, k #) -> case go 0# s1 of + s2 -> (# s2, k `plusWord#` (int2Word# li `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT#) #) + where + !sz = wordArraySize# a + !li = sz -# 1# + + writeMSB = wordToMutableByteArrayLE# (indexWordArray# a li) + mba (moff `plusWord#` int2Word# (li `uncheckedIShiftL#` WORD_SIZE_BYTES_SHIFT#)) + + go i s + | isTrue# (i <# li) + , off <- int2Word# i `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT# + , w <- indexWordArray# a i + = case wordWriteMutableByteArrayLE# w mba (moff `plusWord#` off) s of + s -> go (i +# 1#) s + + | True + = s + +-- | Write a BigNat in base-256 big-endian representation and return the +-- number of bytes written. +-- +-- Use \"@'bigNatSizeInBase' 256# /i/@\" to compute the exact number of bytes +-- written in advance. In case of @/i/ == 0@, the function will write and report +-- zero bytes written. +bigNatToMutableByteArrayBE# :: BigNat -> MutableByteArray# s -> Word# -> State# s -> (# State# s, Word# #) +bigNatToMutableByteArrayBE# a mba moff s0 + | isTrue# (sz ==# 0#) = (# s0, 0## #) + | msw <- indexWordArray# a (sz -# 1#) + = case wordToMutableByteArrayBE# msw mba moff s0 of + (# s1, k #) -> case go (sz -# 1#) k s1 of + s2 -> (# s2, k `plusWord#` (int2Word# (sz -# 1#) `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT#) #) + where + sz = wordArraySize# a + + go i c s + | 0# <- i + = s + + | w <- indexWordArray# a (i -# 1#) + = case wordWriteMutableByteArrayBE# w mba (moff `plusWord#` c) s of + s' -> go (i -# 1#) + (c `plusWord#` WORD_SIZE_IN_BYTES## ) s' + + +-- | Write a BigNat in base-256 representation and return the +-- number of bytes written. +-- +-- The endianness is selected with the Bool# parameter: most significant +-- byte first (big-endian) if @1#@ or least significant byte first +-- (little-endian) if @0#@. +-- +-- Use \"@'bigNatSizeInBase' 256# /i/@\" to compute the exact number of bytes +-- written in advance. In case of @/i/ == 0@, the function will write and report +-- zero bytes written. +bigNatToMutableByteArray# :: BigNat -> MutableByteArray# s -> Word# -> Bool# -> State# s -> (# State# s, Word# #) +bigNatToMutableByteArray# a mba off 0# s = bigNatToMutableByteArrayLE# a mba off s +bigNatToMutableByteArray# a mba off _ s = bigNatToMutableByteArrayBE# a mba off s + +------------------------------------------------- +-- Import from ByteArray +------------------------------------------------- + +-- | Read a BigNat in base-256 little-endian representation from a ByteArray#. +-- +-- The size is given in bytes. +-- +-- Null higher limbs are automatically trimed. +bigNatFromByteArrayLE# :: Word# -> ByteArray# -> Word# -> State# s -> (# State# s, BigNat #) +bigNatFromByteArrayLE# 0## _ _ s = (# s, bigNatZero void# #) +bigNatFromByteArrayLE# sz ba moff s = + let + !nw = sz `uncheckedShiftRL#` WORD_SIZE_BYTES_SHIFT# + !nb = sz `and#` WORD_SIZE_BYTES_MASK## + + readMSB mwa s + | 0## <- nb + = s + + | off <- nw `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT# + = case wordFromByteArrayLE# nb ba (moff `plusWord#` off) of + w -> mwaWrite# mwa (word2Int# nw) w s + + go mwa i s + | isTrue# (i `eqWord#` nw) + = s + + | off <- i `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT# + = case wordFromByteArrayLE# WORD_SIZE_IN_BYTES## ba (moff `plusWord#` off) of + w -> case mwaWrite# mwa (word2Int# i) w s of + s -> go mwa (i `plusWord#` 1##) s + + in case newWordArray# (word2Int# nw +# (word2Int# nb /=# 0#)) s of + (# s, mwa #) -> case readMSB mwa s of + s -> case go mwa 0## s of + s -> case mwaTrimZeroes# mwa s of + s -> unsafeFreezeByteArray# mwa s + +-- | Read a BigNat in base-256 big-endian representation from a ByteArray#. +-- +-- The size is given in bytes. +-- +-- Null higher limbs are automatically trimed. +bigNatFromByteArrayBE# :: Word# -> ByteArray# -> Word# -> State# s -> (# State# s, BigNat #) +bigNatFromByteArrayBE# 0## _ _ s = (# s, bigNatZero void# #) +bigNatFromByteArrayBE# sz ba moff s = + let + !nw = sz `uncheckedShiftRL#` WORD_SIZE_BYTES_SHIFT# + !nb = sz `and#` WORD_SIZE_BYTES_MASK## + + goMSB mwa s + | 0## <- nb + = s + + | True + = case wordFromByteArrayBE# nb ba moff of + w -> mwaWrite# mwa (word2Int# nw) w s + + go mwa i s + | isTrue# (i `eqWord#` nw) + = s + + | k <- nw `minusWord#` 1## `minusWord#` i + , off <- (k `uncheckedShiftL#` WORD_SIZE_BYTES_SHIFT#) `plusWord#` nb + = case wordFromByteArrayBE# WORD_SIZE_IN_BYTES## ba (moff `plusWord#` off) of + w -> case mwaWrite# mwa (word2Int# i) w s of + s -> go mwa (i `plusWord#` 1##) s + + in case newWordArray# (word2Int# nw +# (word2Int# nb /=# 0#)) s of + (# s, mwa #) -> case goMSB mwa s of + s -> case go mwa 0## s of + s -> case mwaTrimZeroes# mwa s of + s -> unsafeFreezeByteArray# mwa s + +-- | Read a BigNat in base-256 representation from a ByteArray#. +-- +-- The size is given in bytes. +-- +-- The endianness is selected with the Bool# parameter: most significant +-- byte first (big-endian) if @1#@ or least significant byte first +-- (little-endian) if @0#@. +-- +-- Null higher limbs are automatically trimed. +bigNatFromByteArray# :: Word# -> ByteArray# -> Word# -> Bool# -> State# s -> (# State# s, BigNat #) +bigNatFromByteArray# sz ba off 0# s = bigNatFromByteArrayLE# sz ba off s +bigNatFromByteArray# sz ba off _ s = bigNatFromByteArrayBE# sz ba off s |