-- | -- Module: Math.NumberTheory.Euclidean -- Copyright: (c) 2018 Alexandre Rodrigues Baldé -- Licence: MIT -- Maintainer: Alexandre Rodrigues Baldé -- Stability: Provisional -- Portability: Non-portable (GHC extensions) -- -- This module exports a class to represent Euclidean domains. -- {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE ScopedTypeVariables #-} module Math.NumberTheory.Euclidean ( Euclidean(..) , WrappedIntegral(..) ) where import Prelude hiding (divMod, div, gcd, lcm, mod, quotRem, quot, rem) import qualified Prelude as P import GHC.Exts import GHC.Integer.GMP.Internals import Numeric.Natural -- | A class to represent a Euclidean domain, -- which is basically an 'Integral' without 'toInteger'. class (Eq a, Num a) => Euclidean a where -- | When restriced to a subring of the Euclidean domain @a@ isomorphic to -- @Integer@, this function should match @quotRem@ for Integers. quotRem :: a -> a -> (a, a) -- | When restriced to a subring of the Euclidean domain @a@ isomorphic to -- @Integer@, this function should match @divMod@ for Integers. divMod :: a -> a -> (a, a) quot :: a -> a -> a quot x y = fst (quotRem x y) rem :: a -> a -> a rem x y = snd (quotRem x y) div :: a -> a -> a div x y = fst (divMod x y) mod :: a -> a -> a mod x y = snd (divMod x y) -- | @'gcd' x y@ is the greatest number that divides both @x@ and @y@. gcd :: a -> a -> a gcd x y = gcd' (abs x) (abs y) where gcd' :: a -> a -> a gcd' a 0 = a gcd' a b = gcd' b (abs (a `mod` b)) -- | @'lcm' x y@ is the smallest number that both @x@ and @y@ divide. lcm :: a -> a -> a lcm _ 0 = 0 lcm 0 _ = 0 lcm x y = abs ((x `quot` (gcd x y)) * y) -- | Test whether two numbers are coprime . coprime :: a -> a -> Bool coprime x y = gcd x y == 1 -- | Calculate the greatest common divisor of two numbers and coefficients -- for the linear combination. -- -- For signed types satisfies: -- -- > case extendedGCD a b of -- > (d, u, v) -> u*a + v*b == d -- > && d == gcd a b -- -- For unsigned and bounded types the property above holds, but since @u@ and @v@ must also be unsigned, -- the result may look weird. E. g., on 64-bit architecture -- -- > extendedGCD (2 :: Word) (3 :: Word) == (1, 2^64-1, 1) -- -- For unsigned and unbounded types (like 'Numeric.Natural.Natural') the result is undefined. -- -- For signed types we also have -- -- > abs u < abs b || abs b <= 1 -- > -- > abs v < abs a || abs a <= 1 -- -- (except if one of @a@ and @b@ is 'minBound' of a signed type). extendedGCD :: a -> a -> (a, a, a) extendedGCD a b = (d, x * signum a, y * signum b) where (d, x, y) = eGCD 0 1 1 0 (abs a) (abs b) eGCD !n1 o1 !n2 o2 r s | s == 0 = (r, o1, o2) | otherwise = case r `quotRem` s of (q, t) -> eGCD (o1 - q*n1) n1 (o2 - q*n2) n2 s t coprimeIntegral :: Integral a => a -> a -> Bool coprimeIntegral x y = (odd x || odd y) && P.gcd x y == 1 -- | Wrapper around 'Integral', which has an 'Eucledian' instance. newtype WrappedIntegral a = WrappedIntegral { unWrappedIntegral :: a } deriving (Eq, Ord, Show, Num, Integral, Real, Enum) instance Integral a => Euclidean (WrappedIntegral a) where quotRem = P.quotRem divMod = P.divMod quot = P.quot rem = P.rem div = P.div mod = P.mod gcd = P.gcd lcm = P.lcm coprime = coprimeIntegral instance Euclidean Int where quotRem = P.quotRem divMod = P.divMod quot = P.quot rem = P.rem div = P.div mod = P.mod gcd (I# x) (I# y) = I# (gcdInt x y) lcm = P.lcm coprime = coprimeIntegral instance Euclidean Word where quotRem = P.quotRem divMod = P.divMod quot = P.quot rem = P.rem div = P.div mod = P.mod gcd (W# x) (W# y) = W# (gcdWord x y) lcm = P.lcm coprime = coprimeIntegral instance Euclidean Integer where quotRem = P.quotRem divMod = P.divMod quot = P.quot rem = P.rem div = P.div mod = P.mod gcd = gcdInteger lcm = lcmInteger coprime = coprimeIntegral -- Blocked by GHC bug -- https://ghc.haskell.org/trac/ghc/ticket/15350 -- extendedGCD = gcdExtInteger instance Euclidean Natural where quotRem = P.quotRem divMod = P.divMod quot = P.quot rem = P.rem div = P.div mod = P.mod gcd = P.gcd lcm = P.lcm coprime = coprimeIntegral