algebra-4.3: src/Numeric/Domain/Internal.hs
{-# LANGUAGE NoImplicitPrelude, FlexibleInstances, UndecidableInstances, DefaultSignatures #-}
module Numeric.Domain.Internal where
import Data.Maybe(fromJust)
import Numeric.Additive.Group
import Numeric.Algebra.Class
import Numeric.Algebra.Commutative
import Numeric.Algebra.Division
import Numeric.Natural (Natural)
import Numeric.Semiring.ZeroProduct
import Numeric.Algebra.Unital.UnitNormalForm
import Numeric.Ring.Class
import Numeric.Decidable.Zero
import Numeric.Decidable.Units
import Prelude (Integer, Maybe (..), Bool(..),
otherwise, fst, snd, ($), (.))
import qualified Prelude as P
infixl 7 `quot`, `rem`
infix 7 `divide`, `divides`, `maybeQuot`
-- | (Integral) domain is the integral semiring.
class (ZeroProductSemiring d, Ring d) => Domain d
instance (ZeroProductSemiring d, Ring d) => Domain d
-- | An integral domain is a commutative domain in which 1≠0.
class (Domain d, Commutative d) => IntegralDomain d where
divides :: d -> d -> Bool
default divides :: (Euclidean d) => d -> d -> Bool
m `divides` n
| isZero m = False
| otherwise = isZero (n `rem` m)
maybeQuot :: d -> d -> Maybe d
default maybeQuot :: (Euclidean d) => d -> d -> Maybe d
m `maybeQuot` n
| isZero n = Nothing
| otherwise = let (q,r) = m `divide` n in
if isZero r then Just q else Nothing
instance IntegralDomain Integer
class (IntegralDomain d, UnitNormalForm d, DecidableZero d) => GCDDomain d where
gcd :: d -> d -> d
default gcd :: (PID d) => d -> d -> d
gcd a b = let (r,_,_) = egcd a b in r
{-# INLINE gcd #-}
reduceFraction :: d -> d -> (d,d)
reduceFraction a b =
let c = gcd a b in
(fromJust (a `maybeQuot` c), fromJust (b `maybeQuot` c))
lcm :: d -> d -> d
lcm p q = fromJust $ (p * q) `maybeQuot` (gcd p q)
instance GCDDomain Integer
class (GCDDomain d) => UFD d
instance UFD Integer
class (UFD d) => PID d where
egcd :: d -> d -> (d,d,d)
default egcd :: (Euclidean d) => d -> d -> (d,d,d)
egcd a b = P.head (euclid a b)
{-# INLINE egcd #-}
instance PID Integer
class (PID d) => Euclidean d where
-- | Euclidean (degree) function on @r@.
degree :: d -> Maybe Natural
default degree :: (Division d) => d -> Maybe Natural
degree a | isZero a = Nothing
| otherwise = Just zero
-- | Division algorithm. @a `divide` b@ calculates
-- quotient and remainder of @a@ divided by @b@.
--
-- prop> let (q, r) = divide a p in p*q + r == a && degree r < degree q
divide :: d -- ^ elements divided by
-> d -- ^ divisor
-> (d,d) -- ^ quotient and remainder
default divide :: (Division d) => d -> d -> (d,d)
-- Be strict in order to make sure division by zero gets caught
divide a b = let q = a/b in (q,P.seq q zero)
quot :: d -> d -> d
quot a b = fst $ a `divide` b
{-# INLINE quot #-}
rem :: d -> d -> d
rem a b = snd $ a `divide` b
{-# INLINE rem #-}
instance Euclidean Integer where
degree = Just . P.fromInteger . P.abs
{-# INLINE degree #-}
divide = P.divMod
{-# INLINE divide #-}
-- | Extended euclidean algorithm.
--
-- prop> euclid f g == xs ==> all (\(r, s, t) -> r == f * s + g * t) xs
euclid :: (Euclidean d) => d -> d -> [(d,d,d)]
euclid f g =
let (ug, g') = splitUnit g
Just t' = recipUnit ug
(uf, f') = splitUnit f
Just s = recipUnit uf
in step [(g', zero, t'), (f', s, zero)]
where
step acc@((r',s',t'):(r,s,t):_)
| isZero r' = P.tail acc
| otherwise =
let q = r `quot` r'
(ur, r'') = splitUnit $ r - q * r'
Just u = recipUnit ur
s'' = (s - q * s') * u
t'' = (t - q * t') * u
in step ((r'', s'', t'') : acc)
step _ = P.error "cannot happen!"