{-# LANGUAGE NoImplicitPrelude #-} module Algebra.IntegralDomain ( {- * Class -} C, div, mod, divMod, {- * Derived functions -} divModZero, divides, sameResidueClass, divChecked, safeDiv, even, odd, divUp, roundDown, roundUp, {- * Algorithms -} decomposeVarPositional, decomposeVarPositionalInf, {- * Properties -} propInverse, propMultipleDiv, propMultipleMod, propProjectAddition, propProjectMultiplication, propUniqueRepresentative, propZeroRepresentative, propSameResidueClass, ) where import qualified Algebra.Ring as Ring -- import qualified Algebra.Additive as Additive import qualified Algebra.ZeroTestable as ZeroTestable import Algebra.Ring ((*), fromInteger, ) import Algebra.Additive (zero, (+), (-), negate, ) import Algebra.ZeroTestable (isZero, ) import Data.Bool.HT (implies, ) import Data.List (mapAccumL, ) import Test.QuickCheck ((==>), Property) import Data.Int (Int, Int8, Int16, Int32, Int64, ) import Data.Word (Word, Word8, Word16, Word32, Word64, ) import NumericPrelude.Base import Prelude (Integer, ) import qualified Prelude as P infixl 7 `div`, `mod` {- Shall we require @ a `mod` 0 === a @ (divModZero) or @ a `mod` 0 === undefined @ ? -} {- | @IntegralDomain@ corresponds to a commutative ring, where @a `mod` b@ picks a canonical element of the equivalence class of @a@ in the ideal generated by @b@. 'div' and 'mod' satisfy the laws > a * b === b * a > (a `div` b) * b + (a `mod` b) === a > (a+k*b) `mod` b === a `mod` b > 0 `mod` b === 0 Typical examples of @IntegralDomain@ include integers and polynomials over a field. Note that for a field, there is a canonical instance defined by the above rules; e.g., > instance IntegralDomain.C Rational where > divMod a b = > if isZero b > then (undefined,a) > else (a\/b,0) It shall be noted, that 'div', 'mod', 'divMod' have a parameter order which is unfortunate for partial application. But it is adapted to mathematical conventions, where the operators are used in infix notation. Minimal definition: 'divMod' or ('div' and 'mod') -} class (Ring.C a) => C a where {-# MINIMAL divMod | (div, mod) #-} div, mod :: a -> a -> a divMod :: a -> a -> (a,a) {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div a b = fst (divMod a b) mod a b = snd (divMod a b) divMod a b = (div a b, mod a b) {-# INLINE divides #-} divides :: (C a, ZeroTestable.C a) => a -> a -> Bool divides y x = isZero (mod x y) {-# INLINE sameResidueClass #-} sameResidueClass :: (C a, ZeroTestable.C a) => a -> a -> a -> Bool sameResidueClass m x y = divides m (x-y) {- | @decomposeVarPositional [b0,b1,b2,...] x@ decomposes @x@ into a positional representation with mixed bases @x0 + b0*(x1 + b1*(x2 + b2*x3))@ E.g. @decomposeVarPositional (repeat 10) 123 == [3,2,1]@ -} decomposeVarPositional :: (C a, ZeroTestable.C a) => [a] -> a -> [a] decomposeVarPositional bs x = map fst $ takeWhile (not . isZero . snd) $ decomposeVarPositionalInfAux bs x decomposeVarPositionalInf :: (C a) => [a] -> a -> [a] decomposeVarPositionalInf bs = map fst . decomposeVarPositionalInfAux bs decomposeVarPositionalInfAux :: (C a) => [a] -> a -> [(a,a)] decomposeVarPositionalInfAux bs x = let (endN,digits) = mapAccumL (\n b -> let (q,r) = divMod n b in (q,(r,n))) x bs in digits ++ [(endN,endN)] {- | Returns the result of the division, if divisible. Otherwise undefined. -} {-# INLINE divChecked #-} divChecked, safeDiv :: (ZeroTestable.C a, C a) => a -> a -> a divChecked a b = let (q,r) = divMod a b in if isZero r then q else error "safeDiv: indivisible term" {-# DEPRECATED safeDiv "use divChecked instead" #-} safeDiv = divChecked {- | Allows division by zero. If the divisor is zero, then the dividend is returned as remainder. -} {-# INLINE divModZero #-} divModZero :: (C a, ZeroTestable.C a) => a -> a -> (a,a) divModZero x y = if isZero y then (zero,x) else divMod x y {-# INLINE even #-} {-# INLINE odd #-} even, odd :: (C a, ZeroTestable.C a) => a -> Bool even n = divides 2 n odd = not . even {- | @roundDown n m@ rounds @n@ down to the next multiple of @m@. That is, @roundDown n m@ is the greatest multiple of @m@ that is at most @n@. The parameter order is consistent with @div@ and friends, but maybe not useful for partial application. -} roundDown :: C a => a -> a -> a roundDown n m = n - mod n m {- | @roundUp n m@ rounds @n@ up to the next multiple of @m@. That is, @roundUp n m@ is the greatest multiple of @m@ that is at most @n@. -} roundUp :: C a => a -> a -> a roundUp n m = n + mod (-n) m {- | @divUp n m@ is similar to @div@ but it rounds up the quotient, such that @divUp n m * m = roundUp n m@. -} divUp :: C a => a -> a -> a divUp n m = - div (-n) m {- What sign of the remainder is most appropriate? divModUp :: C a => a -> a -> (a,a) divModUp n m = mapFst negate $ divMod (-n) m -} {- * Instances for atomic types -} instance C Integer where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Int where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Int8 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Int16 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Int32 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Int64 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Word where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Word8 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Word16 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Word32 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod instance C Word64 where {-# INLINE div #-} {-# INLINE mod #-} {-# INLINE divMod #-} div = P.div mod = P.mod divMod = P.divMod -- Ring.propCommutative and ... propInverse :: (Eq a, C a, ZeroTestable.C a) => a -> a -> Property propMultipleDiv :: (Eq a, C a, ZeroTestable.C a) => a -> a -> Property propMultipleMod :: (Eq a, C a, ZeroTestable.C a) => a -> a -> Property propProjectAddition :: (Eq a, C a, ZeroTestable.C a) => a -> a -> a -> Property propProjectMultiplication :: (Eq a, C a, ZeroTestable.C a) => a -> a -> a -> Property propSameResidueClass :: (Eq a, C a, ZeroTestable.C a) => a -> a -> a -> Property propUniqueRepresentative :: (Eq a, C a, ZeroTestable.C a) => a -> a -> a -> Property propZeroRepresentative :: (Eq a, C a, ZeroTestable.C a) => a -> Property propInverse m a = not (isZero m) ==> (a `div` m) * m + (a `mod` m) == a propMultipleDiv m a = not (isZero m) ==> (a*m) `div` m == a propMultipleMod m a = not (isZero m) ==> (a*m) `mod` m == 0 propProjectAddition m a b = not (isZero m) ==> (a+b) `mod` m == ((a`mod`m)+(b`mod`m)) `mod` m propProjectMultiplication m a b = not (isZero m) ==> (a*b) `mod` m == ((a`mod`m)*(b`mod`m)) `mod` m propUniqueRepresentative m k a = not (isZero m) ==> (a+k*m) `mod` m == a `mod` m propZeroRepresentative m = not (isZero m) ==> zero `mod` m == zero propSameResidueClass m a b = not (isZero m) ==> a `mod` m == b `mod` m `implies` sameResidueClass m a b