diff options
author | Ian Lynagh <igloo@earth.li> | 2008-04-25 02:48:24 +0000 |
---|---|---|
committer | Ian Lynagh <igloo@earth.li> | 2008-04-25 02:48:24 +0000 |
commit | ec042d3bf92b9555391a8ec008a18bded2e55109 (patch) | |
tree | 16160f8a08fa0fdb9c1fa18bc8c006493a74f5dd /libraries/integer-simple/GHC/Integer.hs | |
download | haskell-ec042d3bf92b9555391a8ec008a18bded2e55109.tar.gz |
Initial commit
Diffstat (limited to 'libraries/integer-simple/GHC/Integer.hs')
-rw-r--r-- | libraries/integer-simple/GHC/Integer.hs | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/libraries/integer-simple/GHC/Integer.hs b/libraries/integer-simple/GHC/Integer.hs new file mode 100644 index 0000000000..1c26519e07 --- /dev/null +++ b/libraries/integer-simple/GHC/Integer.hs @@ -0,0 +1,696 @@ + +{-# OPTIONS_GHC -fno-implicit-prelude #-} + +----------------------------------------------------------------------------- +-- | +-- Module : GHC.Integer +-- Copyright : (c) Ian Lnyagh 2007-2008 +-- License : BSD3 +-- +-- Maintainer : igloo@earth.li +-- Stability : internal +-- Portability : non-portable (GHC Extensions) +-- +-- An simple definition of the 'Integer' type. +-- +----------------------------------------------------------------------------- + +#include "MachDeps.h" + +module GHC.Integer ( + Integer, + smallInteger, wordToInteger, integerToWord, toInt#, +#if WORD_SIZE_IN_BITS < 64 + integerToWord64, word64ToInteger, + integerToInt64, int64ToInteger, +#endif + plusInteger, minusInteger, timesInteger, negateInteger, + eqInteger, neqInteger, absInteger, signumInteger, + leInteger, gtInteger, ltInteger, geInteger, compareInteger, + divModInteger, quotRemInteger, quotInteger, remInteger, + encodeFloatInteger, decodeFloatInteger, floatFromInteger, + encodeDoubleInteger, decodeDoubleInteger, doubleFromInteger, + -- gcdInteger, lcmInteger, -- XXX + andInteger, orInteger, xorInteger, complementInteger, + hashInteger, + ) where + +import GHC.Bool +import GHC.Ordering +import GHC.Prim +#if WORD_SIZE_IN_BITS < 64 +import GHC.IntWord64 +#endif + +#if !defined(__HADDOCK__) + +errorInteger :: Integer +errorInteger = Positive errorPositive + +errorPositive :: Positive +errorPositive = Some 47## None -- Random number + +data Integer = Positive Positive | Negative Positive | Naught + +smallInteger :: Int# -> Integer +smallInteger i = if i >=# 0# then wordToInteger (int2Word# i) + else -- XXX is this right for -minBound? + negateInteger (wordToInteger (int2Word# (negateInt# i))) + +wordToInteger :: Word# -> Integer +wordToInteger w = if w `eqWord#` 0## + then Naught + else Positive (Some w None) + +integerToWord :: Integer -> Word# +integerToWord (Positive (Some w _)) = w +integerToWord (Negative (Some w _)) = 0## `minusWord#` w +-- Must be Naught by the invariant: +integerToWord _ = 0## + +toInt# :: Integer -> Int# +toInt# i = word2Int# (integerToWord i) + +#if WORD_SIZE_IN_BITS == 64 +-- Nothing +#elif WORD_SIZE_IN_BITS == 32 +integerToWord64 :: Integer -> Word64# +integerToWord64 i = int64ToWord64# (integerToInt64 i) + +word64ToInteger:: Word64# -> Integer +word64ToInteger w = if w `eqWord64#` wordToWord64# 0## + then Naught + else Positive (word64ToPositive w) + +integerToInt64 :: Integer -> Int64# +integerToInt64 Naught = intToInt64# 0# +integerToInt64 (Positive p) = word64ToInt64# (positiveToWord64 p) +integerToInt64 (Negative p) + = negateInt64# (word64ToInt64# (positiveToWord64 p)) + +int64ToInteger :: Int64# -> Integer +int64ToInteger i + = if i `eqInt64#` intToInt64# 0# + then Naught + else if i `gtInt64#` intToInt64# 0# + then Positive (word64ToPositive (int64ToWord64# i)) + else Negative (word64ToPositive (int64ToWord64# (negateInt64# i))) +#else +#error WORD_SIZE_IN_BITS not supported +#endif + +oneInteger :: Integer +oneInteger = Positive onePositive + +negativeOneInteger :: Integer +negativeOneInteger = Negative onePositive + +twoToTheThirtytwoInteger :: Integer +twoToTheThirtytwoInteger = Positive twoToTheThirtytwoPositive + +encodeDoubleInteger :: Integer -> Int# -> Double# +encodeDoubleInteger i j + = encodeDouble# (toInt# (i `quotInteger` twoToTheThirtytwoInteger)) + (toInt# i) + j + +foreign import ccall unsafe "__2Int_encodeDouble" + encodeDouble# :: Int# -> Int# -> Int# -> Double# + +encodeFloatInteger :: Integer -> Int# -> Float# +encodeFloatInteger i j = encodeFloat# (toInt# i) j + +foreign import ccall unsafe "__int_encodeFloat" + encodeFloat# :: Int# -> Int# -> Float# + +decodeFloatInteger :: Float# -> (# Integer, Int# #) +decodeFloatInteger f = case decodeFloat_Int# f of + (# mant, exp #) -> (# smallInteger mant, exp #) + +decodeDoubleInteger :: Double# -> (# Integer, Int# #) +decodeDoubleInteger d + = case decodeDouble_2Int# d of + (# mant_high#, mant_low#, exp# #) -> + (# (smallInteger mant_high# `timesInteger` twoToTheThirtytwoInteger) + `plusInteger` wordToInteger (int2Word# mant_low#), + exp# #) + +doubleFromInteger :: Integer -> Double# +doubleFromInteger Naught = 0.0## +doubleFromInteger (Positive p) = doubleFromPositive p +doubleFromInteger (Negative p) = negateDouble# (doubleFromPositive p) + +floatFromInteger :: Integer -> Float# +floatFromInteger Naught = 0.0# +floatFromInteger (Positive p) = floatFromPositive p +floatFromInteger (Negative p) = negateFloat# (floatFromPositive p) + +andInteger :: Integer -> Integer -> Integer +Naught `andInteger` _ = Naught +_ `andInteger` Naught = Naught +Positive x `andInteger` Positive y = digitsToInteger (x `andDigits` y) +{- +To calculate x & -y we need to calculate + x & twosComplement y +The (imaginary) sign bits are 0 and 1, so &ing them give 0, i.e. positive. +Note that + twosComplement y +has infinitely many 1s, but x has a finite number of digits, so andDigits +will return a finite result. +-} +Positive x `andInteger` Negative y = let y' = twosComplementPositive y + z = x `andDigits` y' + in digitsToInteger z +Negative x `andInteger` Positive y = Positive y `andInteger` Negative x +{- +To calculate -x & -y, naively we need to calculate + twosComplement (twosComplement x & twosComplement y) +but + twosComplement x & twosComplement y +has infinitely many 1s, so this won't work. Thus we use de Morgan's law +to get + -x & -y = !(!(-x) | !(-y)) + = !(!(twosComplement x) | !(twosComplement y)) + = !(!(!x + 1) | (!y + 1)) + = !((x - 1) | (y - 1)) +but the result is negative, so we need to take the two's complement of +this in order to get the magnitude of the result. + twosComplement !((x - 1) | (y - 1)) + = !(!((x - 1) | (y - 1))) + 1 + = ((x - 1) | (y - 1)) + 1 +-} +-- We don't know that x and y are /strictly/ greater than 1, but +-- minusPositive gives us the required answer anyway. +Negative x `andInteger` Negative y = let x' = x `minusPositive` onePositive + y' = y `minusPositive` onePositive + z = x' `orDigits` y' + -- XXX Cheating the precondition: + z' = succPositive z + in digitsToNegativeInteger z' + +orInteger :: Integer -> Integer -> Integer +Naught `orInteger` i = i +i `orInteger` Naught = i +Positive x `orInteger` Positive y = Positive (x `orDigits` y) +{- +x | -y = - (twosComplement (x | twosComplement y)) + = - (twosComplement !(!x & !(twosComplement y))) + = - (twosComplement !(!x & !(!y + 1))) + = - (twosComplement !(!x & (y - 1))) + = - ((!x & (y - 1)) + 1) +-} +Positive x `orInteger` Negative y = let x' = flipBits x + y' = y `minusPositive` onePositive + z = x' `andDigits` y' + z' = succPositive z + in digitsToNegativeInteger z' +Negative x `orInteger` Positive y = Positive y `orInteger` Negative x +{- +-x | -y = - (twosComplement (twosComplement x | twosComplement y)) + = - (twosComplement !(!(twosComplement x) & !(twosComplement y))) + = - (twosComplement !(!(!x + 1) & !(!y + 1))) + = - (twosComplement !((x - 1) & (y - 1))) + = - (((x - 1) & (y - 1)) + 1) +-} +Negative x `orInteger` Negative y = let x' = x `minusPositive` onePositive + y' = y `minusPositive` onePositive + z = x' `andDigits` y' + z' = succPositive z + in digitsToNegativeInteger z' + +xorInteger :: Integer -> Integer -> Integer +Naught `xorInteger` i = i +i `xorInteger` Naught = i +Positive x `xorInteger` Positive y = digitsToInteger (x `xorDigits` y) +{- +x ^ -y = - (twosComplement (x ^ twosComplement y)) + = - (twosComplement !(x ^ !(twosComplement y))) + = - (twosComplement !(x ^ !(!y + 1))) + = - (twosComplement !(x ^ (y - 1))) + = - ((x ^ (y - 1)) + 1) +-} +Positive x `xorInteger` Negative y = let y' = y `minusPositive` onePositive + z = x `xorDigits` y' + z' = succPositive z + in digitsToNegativeInteger z' +Negative x `xorInteger` Positive y = Positive y `xorInteger` Negative x +{- +-x ^ -y = twosComplement x ^ twosComplement y + = (!x + 1) ^ (!y + 1) + = (!x + 1) ^ (!y + 1) + = !(!x + 1) ^ !(!y + 1) + = (x - 1) ^ (y - 1) +-} +Negative x `xorInteger` Negative y = let x' = x `minusPositive` onePositive + y' = y `minusPositive` onePositive + z = x' `xorDigits` y' + in digitsToInteger z + +complementInteger :: Integer -> Integer +complementInteger x = negativeOneInteger `minusInteger` x + +-- Requires a finite Positive. +-- Returns an infinite Positive. +twosComplementPositive :: Positive -> Positive +twosComplementPositive p = succPositive (flipBits p) + +-- Requires a finite Positive. +-- Returns an infinite Positive. +flipBits :: Positive -> Positive +flipBits None = let ones = Some (fullBound Unit) ones + in ones +flipBits (Some w ws) = Some (not# w) (flipBits ws) + +negateInteger :: Integer -> Integer +negateInteger (Positive p) = Negative p +negateInteger (Negative p) = Positive p +negateInteger Naught = Naught + +plusInteger :: Integer -> Integer -> Integer +Positive p1 `plusInteger` Positive p2 = Positive (p1 `plusPositive` p2) +Negative p1 `plusInteger` Negative p2 = Negative (p1 `plusPositive` p2) +Positive p1 `plusInteger` Negative p2 = case p1 `comparePositive` p2 of + GT -> Positive (p1 `minusPositive` p2) + EQ -> Naught + LT -> Negative (p2 `minusPositive` p1) +Negative p1 `plusInteger` Positive p2 = Positive p2 `plusInteger` Negative p1 +Naught `plusInteger` i = i +i `plusInteger` Naught = i + +minusInteger :: Integer -> Integer -> Integer +i1 `minusInteger` i2 = i1 `plusInteger` negateInteger i2 + +timesInteger :: Integer -> Integer -> Integer +Positive p1 `timesInteger` Positive p2 = Positive (p1 `timesPositive` p2) +Negative p1 `timesInteger` Negative p2 = Positive (p1 `timesPositive` p2) +Positive p1 `timesInteger` Negative p2 = Negative (p1 `timesPositive` p2) +Negative p1 `timesInteger` Positive p2 = Negative (p1 `timesPositive` p2) +_ `timesInteger` _ = Naught + +divModInteger :: Integer -> Integer -> (# Integer, Integer #) +n `divModInteger` d = + case n `quotRemInteger` d of + (# q, r #) -> + if signumInteger r `eqInteger` + negateInteger (signumInteger d) + then (# q `minusInteger` oneInteger, r `plusInteger` d #) + else (# q, r #) + +quotRemInteger :: Integer -> Integer -> (# Integer, Integer #) +Naught `quotRemInteger` _ = (# Naught, Naught #) +_ `quotRemInteger` Naught + = (# errorInteger, errorInteger #) -- XXX Can't happen +-- XXX _ `quotRemInteger` Naught = error "Division by zero" +Positive p1 `quotRemInteger` Positive p2 = p1 `quotRemPositive` p2 +Negative p1 `quotRemInteger` Positive p2 = case p1 `quotRemPositive` p2 of + (# q, r #) -> + (# negateInteger q, + negateInteger r #) +Positive p1 `quotRemInteger` Negative p2 = case p1 `quotRemPositive` p2 of + (# q, r #) -> + (# negateInteger q, r #) +Negative p1 `quotRemInteger` Negative p2 = case p1 `quotRemPositive` p2 of + (# q, r #) -> + (# q, negateInteger r #) + +quotInteger :: Integer -> Integer -> Integer +x `quotInteger` y = case x `quotRemInteger` y of + (# q, _ #) -> q + +remInteger :: Integer -> Integer -> Integer +x `remInteger` y = case x `quotRemInteger` y of + (# _, r #) -> r + +compareInteger :: Integer -> Integer -> Ordering +Positive x `compareInteger` Positive y = x `comparePositive` y +Positive _ `compareInteger` _ = GT +Naught `compareInteger` Naught = EQ +Naught `compareInteger` Negative _ = GT +Negative x `compareInteger` Negative y = y `comparePositive` x +_ `compareInteger` _ = LT + +eqInteger :: Integer -> Integer -> Bool +x `eqInteger` y = case x `compareInteger` y of + EQ -> True + _ -> False + +neqInteger :: Integer -> Integer -> Bool +x `neqInteger` y = case x `compareInteger` y of + EQ -> False + _ -> True + +ltInteger :: Integer -> Integer -> Bool +x `ltInteger` y = case x `compareInteger` y of + LT -> True + _ -> False + +gtInteger :: Integer -> Integer -> Bool +x `gtInteger` y = case x `compareInteger` y of + GT -> True + _ -> False + +leInteger :: Integer -> Integer -> Bool +x `leInteger` y = case x `compareInteger` y of + GT -> False + _ -> True + +geInteger :: Integer -> Integer -> Bool +x `geInteger` y = case x `compareInteger` y of + LT -> False + _ -> True + +absInteger :: Integer -> Integer +absInteger (Negative x) = Positive x +absInteger x = x + +signumInteger :: Integer -> Integer +signumInteger (Negative _) = negativeOneInteger +signumInteger Naught = Naught +signumInteger (Positive _) = oneInteger + +-- XXX This isn't a great hash function +hashInteger :: Integer -> Int# +hashInteger _ = 42# + +------------------------------------------------------------------- +-- The hard work is done on positive numbers + +-- Least significant bit is first + +-- Positive's have the property that they contain at least one Bit, +-- and their last Bit is One. +type Positive = Digits +type Positives = List Positive + +data Digits = Some Digit Digits + | None +type Digit = Word# + +-- XXX Could move () above us +data Unit = Unit + +-- XXX Could move [] above us +data List a = Nil | Cons a (List a) + +onePositive :: Positive +onePositive = Some 1## None + +halfBoundUp, fullBound :: Unit -> Digit +lowHalfMask :: Unit -> Digit +highHalfShift :: Unit -> Int# +twoToTheThirtytwoPositive :: Positive +#if WORD_SIZE_IN_BITS == 64 +halfBoundUp Unit = 0x8000000000000000## +fullBound Unit = 0xFFFFFFFFFFFFFFFF## +lowHalfMask Unit = 0xFFFFFFFF## +highHalfShift Unit = 32# +twoToTheThirtytwoPositive = Some 0x100000000## None +#elif WORD_SIZE_IN_BITS == 32 +halfBoundUp Unit = 0x80000000## +fullBound Unit = 0xFFFFFFFF## +lowHalfMask Unit = 0xFFFF## +highHalfShift Unit = 16# +twoToTheThirtytwoPositive = Some 0## (Some 1## None) +#else +#error Unhandled WORD_SIZE_IN_BITS +#endif + +digitsMaybeZeroToInteger :: Digits -> Integer +digitsMaybeZeroToInteger None = Naught +digitsMaybeZeroToInteger ds = Positive ds + +digitsToInteger :: Digits -> Integer +digitsToInteger ds = case removeZeroTails ds of + None -> Naught + ds' -> Positive ds' + +digitsToNegativeInteger :: Digits -> Integer +digitsToNegativeInteger ds = case removeZeroTails ds of + None -> Naught + ds' -> Negative ds' + +removeZeroTails :: Digits -> Digits +removeZeroTails (Some w ds) = if w `eqWord#` 0## + then case removeZeroTails ds of + None -> None + ds' -> Some w ds' + else Some w (removeZeroTails ds) +removeZeroTails None = None + +#if WORD_SIZE_IN_BITS < 64 +word64ToPositive :: Word64# -> Positive +word64ToPositive w + = if w `eqWord64#` wordToWord64# 0## + then None + else Some (word64ToWord# w) (word64ToPositive (w `uncheckedShiftRL64#` 32#)) + +positiveToWord64 :: Positive -> Word64# +positiveToWord64 None = wordToWord64# 0## -- XXX Can't happen +positiveToWord64 (Some w None) = wordToWord64# w +positiveToWord64 (Some low (Some high _)) + = wordToWord64# low `or64#` (wordToWord64# high `uncheckedShiftL64#` 32#) +#endif + +comparePositive :: Positive -> Positive -> Ordering +Some x xs `comparePositive` Some y ys = case xs `comparePositive` ys of + EQ -> if x `ltWord#` y then LT + else if x `gtWord#` y then GT + else EQ + res -> res +None `comparePositive` None = EQ +_ `comparePositive` None = GT +None `comparePositive` _ = LT + +plusPositive :: Positive -> Positive -> Positive +plusPositive = addWithCarry 0## + where -- digit `elem` [0, 1] + addWithCarry :: Digit -> Positive -> Positive -> Positive + addWithCarry c xs None = addOnCarry c xs + addWithCarry c None ys = addOnCarry c ys + addWithCarry c xs@(Some x xs') ys@(Some y ys') + = if x `ltWord#` y then addWithCarry c ys xs + -- Now x >= y + else if y `geWord#` halfBoundUp Unit + -- So they are both at least halfBoundUp, so we subtract + -- halfBoundUp from each and thus carry 1 + then case x `minusWord#` halfBoundUp Unit of + x' -> + case y `minusWord#` halfBoundUp Unit of + y' -> + case x' `plusWord#` y' `plusWord#` c of + this -> + Some this withCarry + else if x `geWord#` halfBoundUp Unit + then case x `minusWord#` halfBoundUp Unit of + x' -> + case x' `plusWord#` y `plusWord#` c of + z -> + -- We've taken off halfBoundUp, so now we need to + -- add it back on + if z `ltWord#` halfBoundUp Unit + then Some (z `plusWord#` halfBoundUp Unit) withoutCarry + else Some (z `minusWord#` halfBoundUp Unit) withCarry + else Some (x `plusWord#` y `plusWord#` c) withoutCarry + where withCarry = addWithCarry 1## xs' ys' + withoutCarry = addWithCarry 0## xs' ys' + + -- digit `elem` [0, 1] + addOnCarry :: Digit -> Positive -> Positive + addOnCarry c ws = if c `eqWord#` 0## + then ws + else succPositive ws + +-- digit `elem` [0, 1] +succPositive :: Positive -> Positive +succPositive None = Some 1## None +succPositive (Some w ws) = if w `eqWord#` fullBound Unit + then Some 0## (succPositive ws) + else Some (w `plusWord#` 1##) ws + +-- Requires x > y +-- In recursive calls, x >= y and x == y => result is None +minusPositive :: Positive -> Positive -> Positive +Some x xs `minusPositive` Some y ys + = if x `eqWord#` y + then case xs `minusPositive` ys of + None -> None + s -> Some 0## s + else if x `gtWord#` y then + Some (x `minusWord#` y) (xs `minusPositive` ys) + else case (fullBound Unit `minusWord#` y) `plusWord#` 1## of + z -> -- z = 2^n - y, calculated without overflow + case z `plusWord#` x of + z' -> -- z = 2^n + (x - y), calculated without overflow + Some z' ((xs `minusPositive` ys) `minusPositive` onePositive) +xs `minusPositive` None = xs +None `minusPositive` _ = errorPositive -- XXX Can't happen +-- XXX None `minusPositive` _ = error "minusPositive: Requirement x > y not met" + +timesPositive :: Positive -> Positive -> Positive +-- XXX None's can't happen here: +None `timesPositive` _ = errorPositive +_ `timesPositive` None = errorPositive +-- x and y are the last digits in Positive numbers, so are not 0: +Some x None `timesPositive` Some y None = x `timesDigit` y +xs@(Some _ None) `timesPositive` ys = ys `timesPositive` xs +-- y is the last digit in a Positive number, so is not 0: +Some x xs' `timesPositive` ys@(Some y None) + = -- We could actually skip this test, and everything would + -- turn out OK. We already play tricks like that in timesPositive. + let zs = Some 0## (xs' `timesPositive` ys) + in if x `eqWord#` 0## + then zs + else (x `timesDigit` y) `plusPositive` zs +Some x xs' `timesPositive` ys@(Some _ _) + = (Some x None `timesPositive` ys) `plusPositive` + Some 0## (xs' `timesPositive` ys) + +{- +-- Requires arguments /= 0 +Suppose we have 2n bits in a Word. Then + x = 2^n xh + xl + y = 2^n yh + yl + x * y = (2^n xh + xl) * (2^n yh + yl) + = 2^(2n) (xh yh) + + 2^n (xh yl) + + 2^n (xl yh) + + (xl yl) + ~~~~~~~ - all fit in 2n bits +-} +timesDigit :: Digit -> Digit -> Positive +timesDigit x y + = case splitHalves x of + (# xh, xl #) -> + case splitHalves y of + (# yh, yl #) -> + case xh `timesWord#` yh of + xhyh -> + case splitHalves (xh `timesWord#` yl) of + (# xhylh, xhyll #) -> + case xhyll `uncheckedShiftL#` highHalfShift Unit of + xhyll' -> + case splitHalves (xl `timesWord#` yh) of + (# xlyhh, xlyhl #) -> + case xlyhl `uncheckedShiftL#` highHalfShift Unit of + xlyhl' -> + case xl `timesWord#` yl of + xlyl -> + -- Add up all the high word results. As the result fits in + -- 4n bits this can't overflow. + case xhyh `plusWord#` xhylh `plusWord#` xlyhh of + high -> + -- low: xhyll<<n + xlyhl<<n + xlyl + -- From this point we might make (Some 0 None), but we know + -- that the final result will be positive and the addition + -- will work out OK, so everything will work out in the end. + -- One thing we do need to be careful of is avoiding returning + -- Some 0 (Some 0 None) + Some n None, as this will result in + -- Some n (Some 0 None) instead of Some n None. + let low = Some xhyll' None `plusPositive` + Some xlyhl' None `plusPositive` + Some xlyl None + in if high `eqWord#` 0## + then low + else Some 0## (Some high None) `plusPositive` low + +splitHalves :: Digit -> (# {- High -} Digit, {- Low -} Digit #) +splitHalves x = (# x `uncheckedShiftRL#` highHalfShift Unit, + x `and#` lowHalfMask Unit #) + +-- Assumes 0 <= i <= 31 +shiftLPositive :: Positive -> Int# -> Positive +shiftLPositive None _ = None -- XXX Can't happen +shiftLPositive p i = + case WORD_SIZE_IN_BITS# -# i of + j -> let f carry None = if carry `eqWord#` 0## + then None + else Some carry None + f carry (Some w ws) = case w `uncheckedShiftRL#` j of + carry' -> + case w `uncheckedShiftL#` i of + me -> + Some (me `or#` carry) (f carry' ws) + in f 0## p + +-- Long division +quotRemPositive :: Positive -> Positive -> (# Integer, Integer #) +xs `quotRemPositive` ys + = case f xs of + (# d, m #) -> (# digitsMaybeZeroToInteger d, + digitsMaybeZeroToInteger m #) + where + subtractors :: Positives + subtractors = mkSubtractors (WORD_SIZE_IN_BITS# -# 1#) + + mkSubtractors n = if n ==# 0# + then Cons ys Nil + else Cons (ys `shiftLPositive` n) + (mkSubtractors (n -# 1#)) + + -- The main function. Go the the end of xs, then walk + -- back trying to divide the number we accumulate by ys. + f :: Positive -> (# Digits, Digits #) + f None = (# None, None #) + f (Some z zs) + = case f zs of + (# ds, m #) -> + let -- We need to avoid making (Some Zero None) here + m' = some z m + in case g 0## subtractors m' of + (# d, m'' #) -> + (# some d ds, m'' #) + + g :: Digit -> Positives -> Digits -> (# Digit, Digits #) + g d Nil m = (# d, m #) + g d (Cons sub subs) m + = case d `uncheckedShiftL#` 1# of + d' -> + case m `comparePositive` sub of + LT -> g d' subs m + _ -> g (d' `plusWord#` 1##) + subs + (m `minusPositive` sub) + +some :: Digit -> Digits -> Digits +some w None = if w `eqWord#` 0## then None else Some w None +some w ws = Some w ws + +andDigits :: Digits -> Digits -> Digits +andDigits _ None = None +andDigits None _ = None +andDigits (Some w1 ws1) (Some w2 ws2) = Some (w1 `and#` w2) (andDigits ws1 ws2) + +orDigits :: Digits -> Digits -> Digits +orDigits None ds = ds +orDigits ds None = ds +orDigits (Some w1 ds1) (Some w2 ds2) = Some (w1 `or#` w2) (orDigits ds1 ds2) + +xorDigits :: Digits -> Digits -> Digits +xorDigits None ds = ds +xorDigits ds None = ds +xorDigits (Some w1 ds1) (Some w2 ds2) = Some (w1 `xor#` w2) (xorDigits ds1 ds2) + +-- XXX We'd really like word2Double# for this +doubleFromPositive :: Positive -> Double# +doubleFromPositive None = 0.0## +doubleFromPositive (Some w ds) + = case splitHalves w of + (# h, l #) -> + (doubleFromPositive ds *## (2.0## **## WORD_SIZE_IN_BITS.0##)) + +## (int2Double# (word2Int# h) *## + (2.0## **## int2Double# (highHalfShift Unit))) + +## int2Double# (word2Int# l) + +-- XXX We'd really like word2Float# for this +floatFromPositive :: Positive -> Float# +floatFromPositive None = 0.0# +floatFromPositive (Some w ds) + = case splitHalves w of + (# h, l #) -> + (floatFromPositive ds `timesFloat#` (2.0# `powerFloat#` WORD_SIZE_IN_BITS.0#)) + `plusFloat#` (int2Float# (word2Int# h) `timesFloat#` + (2.0# `powerFloat#` int2Float# (highHalfShift Unit))) + `plusFloat#` int2Float# (word2Int# l) + +#endif + |