----------------------------------------------------------------------------- --- This library contains data types for a binary representation of --- natural and integers. --- It is based on the paper _Declaring Numbers_ by Brassel/Fischer/Huch --- [ENTCS 216, 2008](http://dx.doi.org/10.1016/j.entcs.2008.06.037). --- --- The advantage of this algebraic definition in contrast to built-in --- integers is the possibility to use them also for narrowing, i.e., --- functional logic programming. Since the operations are also quite --- efficient compared to a simple Peano representations, this library --- is also the basis of the implementation of integers in the Curry --- implementation [KiCS2](http://www-ps.informatik.uni-kiel.de/kics2/). --- @version February 2017 ----------------------------------------------------------------------------- module BinInt where ----------------------------------------------------------------------------- -- Nat ----------------------------------------------------------------------------- --- Algebraic data type to represent natural numbers. --- @cons IHi - most significant bit --- @cons O - multiply with 2 --- @cons I - multiply with 2 and add 1 data Nat = IHi | O Nat | I Nat --- Successor, O(n) succ :: Nat -> Nat succ IHi = O IHi -- 1 + 1 = 2 succ (O bs) = I bs -- 2*n + 1 = 2*n + 1 succ (I bs) = O (succ bs) -- 2*n + 1 + 1 = 2*(n+1) --- Predecessor, O(n) pred :: Nat -> Nat pred IHi = failed -- 1 has no predecessor pred (O IHi) = IHi -- 2 - 1 = 1 pred (O x@(O _)) = I (pred x) -- 2*2*n - 1 = 2*(2*n-1) + 1 pred (O (I x)) = I (O x) -- 2*(2*n + 1) - 1 = 2*2*n + 1 pred (I x) = O x -- 2*n + 1 -1 = 2*n --- Addition, O(max (m, n)) (+^) :: Nat -> Nat -> Nat IHi +^ y = succ y -- 1 + n = n + 1 O x +^ IHi = I x -- 2*n + 1 = 2*n + 1 O x +^ O y = O (x +^ y) -- 2*m + 2*n = 2*(m+n) O x +^ I y = I (x +^ y) I x +^ IHi = O (succ x) I x +^ O y = I (x +^ y) I x +^ I y = O (succ x +^ y) --- Subtraction (-^) :: Nat -> Nat -> BinInt IHi -^ y = inc (Neg y) -- 1-n = 1+(-n) x@(O _) -^ IHi = Pos (pred x) -- (O x) -^ (O y) = mult2 (x -^ y) (O x) -^ (I y) = dec (mult2 (x -^ y)) (I x) -^ IHi = Pos (O x) (I x) -^ (O y) = inc (mult2 (x -^ y)) -- 2*n+1 - 2*m = 1+2*(n-m) (I x) -^ (I y) = mult2 (x -^ y) -- 2*n+1 - (2*m+1) = 2*(n-m) --- Multiplication by 2 mult2 :: BinInt -> BinInt mult2 (Pos n) = Pos (O n) mult2 Zero = Zero mult2 (Neg n) = Neg (O n) --- Multiplication, O(m*n) (*^) :: Nat -> Nat -> Nat IHi *^ y = y (O x) *^ y = O (x *^ y) (I x) *^ y = y +^ (O (x *^ y)) -- (I x) *^ IHi = I x -- (I x) *^ (O y) = (O y) +^ (O (x *^ (O y))) = O (y +^ (x *^ (O y))) -- (I x) *^ (I y) = (I y) +^ (O (x *^ (I y))) = I (y +^ (x *^ (I y))) --- Division by 2 div2 :: Nat -> Nat div2 IHi = failed -- 1 div 2 is not defined for Nat div2 (O x) = x div2 (I x) = x --- Modulo by 2 mod2 :: Nat -> BinInt mod2 IHi = Pos IHi mod2 (O _) = Zero mod2 (I _) = Pos IHi --- Comparison of natural numbers, O(min (m,n)) cmpNat :: Nat -> Nat -> Ordering cmpNat IHi IHi = EQ cmpNat IHi (O _) = LT cmpNat IHi (I _) = LT cmpNat (O _) IHi = GT cmpNat (O x) (O y) = cmpNat x y cmpNat (O x) (I y) = case cmpNat x y of EQ -> LT cmpxy -> cmpxy cmpNat (I _) IHi = GT cmpNat (I x) (O y) = case cmpNat x y of EQ -> GT cmpxy -> cmpxy cmpNat (I x) (I y) = cmpNat x y --- Quotient and remainder. quotRemNat :: Nat -> Nat -> (BinInt, BinInt) quotRemNat x y | y == IHi = (Pos x, Zero ) -- quotRemNat x 1 = (x, 0) | x == IHi = (Zero , Pos IHi) -- quotRemNat 1 y = (0, 1) | otherwise = case cmpNat x y of EQ -> (Pos IHi, Zero ) -- x = y : quotRemNat x y = (1, 0) LT -> (Zero , Pos x) -- x < y : quotRemNat x y = (0, x) GT -> case quotRemNat (div2 x) y of (Neg _, _ ) -> error "quotRemNat: negative quotient" (Zero , _ ) -> (Pos IHi , x -^ y) -- x > y, x/2 < y : quotRemNat x y = (1, x - y) (Pos _, Neg _) -> error "quotRemNat: negative remainder" (Pos d, Zero ) -> (Pos (O d), mod2 x) (Pos d, Pos m) -> case quotRemNat (shift x m) y of (Neg _ , _ ) -> error "quotRemNat: negative quotient" (Zero , m') -> (Pos (O d) , m') (Pos d', m') -> (Pos (O d +^ d'), m') where shift IHi _ = error "quotRemNat.shift: IHi" shift (O _) n = O n shift (I _) n = I n ----------------------------------------------------------------------------- -- Integer ----------------------------------------------------------------------------- --- Algebraic data type to represent integers. data BinInt = Neg Nat | Zero | Pos Nat --- Less-than-or-equal on BinInt lteqInteger :: BinInt -> BinInt -> Bool lteqInteger x y = cmpInteger x y /= GT --- Comparison on BinInt, O(min (m, n)) cmpInteger :: BinInt -> BinInt -> Ordering cmpInteger Zero Zero = EQ cmpInteger Zero (Pos _) = LT cmpInteger Zero (Neg _) = GT cmpInteger (Pos _) Zero = GT cmpInteger (Pos x) (Pos y) = cmpNat x y cmpInteger (Pos _) (Neg _) = GT cmpInteger (Neg _) Zero = LT cmpInteger (Neg _) (Pos _) = LT cmpInteger (Neg x) (Neg y) = cmpNat y x --- Unary minus. Usually written as "- e". neg :: BinInt -> BinInt neg Zero = Zero neg (Pos x) = Neg x neg (Neg x) = Pos x --- Increment inc :: BinInt -> BinInt inc Zero = Pos IHi inc (Pos n) = Pos (succ n) inc (Neg IHi) = Zero inc (Neg (O n)) = Neg (pred (O n)) inc (Neg (I n)) = Neg (O n) --- Decrement dec :: BinInt -> BinInt dec Zero = Neg IHi dec (Pos IHi) = Zero dec (Pos (O n)) = Pos (pred (O n)) dec (Pos (I n)) = Pos (O n) dec (Neg n) = Neg (succ n) --- Adds two BinInts. (+#) :: BinInt -> BinInt -> BinInt Zero +# x = x x@(Pos _) +# Zero = x Pos x +# Pos y = Pos (x +^ y) Pos x +# Neg y = x -^ y x@(Neg _) +# Zero = x Neg x +# Pos y = y -^ x Neg x +# Neg y = Neg (x +^ y) --- Subtracts two BinInts. (-#) :: BinInt -> BinInt -> BinInt x -# Zero = x x -# Pos y = x +# Neg y x -# Neg y = x +# Pos y --- Multiplies two BinInts. (*#) :: BinInt -> BinInt -> BinInt Zero *# _ = Zero Pos _ *# Zero = Zero Pos x *# Pos y = Pos (x *^ y) Pos x *# Neg y = Neg (x *^ y) Neg _ *# Zero = Zero Neg x *# Pos y = Neg (x *^ y) Neg x *# Neg y = Pos (x *^ y) --- Quotient and Remainder, truncated against zero quotRemInteger :: BinInt -> BinInt -> (BinInt, BinInt) quotRemInteger _ Zero = failed -- division by zero is not defined quotRemInteger Zero (Pos _) = (Zero, Zero) quotRemInteger Zero (Neg _) = (Zero, Zero) quotRemInteger (Pos x) (Pos y) = quotRemNat x y quotRemInteger (Neg x) (Pos y) = let (d, m) = quotRemNat x y in (neg d, neg m) quotRemInteger (Pos x) (Neg y) = let (d, m) = quotRemNat x y in (neg d, m) quotRemInteger (Neg x) (Neg y) = let (d, m) = quotRemNat x y in (d , neg m) --- Quotient and Remainder, truncated against negative infinity divModInteger :: BinInt -> BinInt -> (BinInt, BinInt) divModInteger _ Zero = failed -- division by zero is not defined divModInteger Zero (Pos _) = (Zero, Zero) divModInteger Zero (Neg _) = (Zero, Zero) divModInteger (Pos x) (Pos y) = quotRemNat x y divModInteger (Neg x) (Pos y) = let (d, m) = quotRemNat x y in case m of Zero -> (neg d, m) _ -> (neg (inc d), (Pos y) -# m) divModInteger (Pos x) (Neg y) = let (d, m) = quotRemNat x y in case m of Zero -> (neg d, m) _ -> (neg (inc d), m -# (Pos y)) divModInteger (Neg x) (Neg y) = let (d, m) = quotRemNat x y in (d, neg m) --- Integer divisor, truncated towards negative infinity. divInteger :: BinInt -> BinInt -> BinInt divInteger x y = fst (divModInteger x y) --- Integer modulo, truncated towards negative infinity. modInteger :: BinInt -> BinInt -> BinInt modInteger x y = snd (divModInteger x y) --- Integer quotient, truncated towards zero. quotInteger :: BinInt -> BinInt -> BinInt quotInteger x y = fst (quotRemInteger x y) --- Integer remainder, truncated towards zero. remInteger :: BinInt -> BinInt -> BinInt remInteger x y = snd (quotRemInteger x y) -----------------------------------------------------------------------------