packages feed

mod 0.2.1.0 → 0.2.2.0

raw patch · 5 files changed

+124/−54 lines, 5 filesdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base

API changes (from Hackage documentation)

- Data.Mod: unMod :: Mod m -> Natural
+ Data.Mod: unMod :: forall (m :: Nat). Mod m -> Natural
- Data.Mod.Word: unMod :: Mod m -> Word
+ Data.Mod.Word: unMod :: forall (m :: Nat). Mod m -> Word

Files

Data/Mod.hs view
@@ -73,17 +73,18 @@ -- 1 -- -- __Note:__ 'Mod' 0 has no inhabitants, eventhough \( \mathbb{Z}/0\mathbb{Z} \) is technically isomorphic to \( \mathbb{Z} \).-newtype Mod (m :: Nat) = Mod-  { unMod :: Natural-  -- ^ The canonical representative of the residue class,-  -- always between 0 and \( m - 1 \) (inclusively).-  ---  -- >>> :set -XDataKinds-  -- >>> -1 :: Mod 10-  -- 9-  }+newtype Mod (m :: Nat) = Mod Natural   deriving (Eq, Ord, Generic) +-- | The canonical representative of the residue class,+-- always between 0 and \( m - 1 \) (inclusively).+--+-- >>> :set -XDataKinds+-- >>> -1 :: Mod 10+-- 9+unMod :: Mod m -> Natural+unMod (Mod w) = w+ instance NFData (Mod m)  instance Show (Mod m) where@@ -268,11 +269,11 @@   gcd (Mod x) (Mod y) = g     where       m = natVal g-      g = Mod $ if m > 1 then Prelude.gcd (Prelude.gcd m x) y else 0+      g = fromIntegral (Prelude.gcd (Prelude.gcd m x) y)   lcm (Mod x) (Mod y) = l     where       m = natVal l-      l = Mod $ if m > 1 then Prelude.lcm (Prelude.gcd m x) (Prelude.gcd m y) else 0+      l = fromIntegral (Prelude.lcm (Prelude.gcd m x) (Prelude.gcd m y))   coprime x y = Data.Euclidean.gcd x y == one  -- | 'Mod' @m@ is not even an@@ -370,19 +371,17 @@ -- 7 -- >>> 4 ^% (-1) :: Mod 10 -- 4 and 10 are not coprime -- (*** Exception: divide by zero-(^%) :: (KnownNat m, Integral a) => Mod m -> a -> Mod m+(^%) :: forall m a. (KnownNat m, Integral a) => Mod m -> a -> Mod m mx ^% a   | a < 0     = case invertMod mx of     Nothing ->  throw DivideByZero-    Just my ->  Mod $ powModNatural (unMod my) (fromIntegral' (-a)) (natVal mx)-  | otherwise = Mod $ powModNatural (unMod mx) (fromIntegral' a)    (natVal mx)+    Just my ->  Mod (powModNatural (unMod my) (absAsNatural a) (natVal mx))+  | otherwise = Mod $ powModNatural (unMod mx) (fromIntegral a) (natVal mx)   where-#if __GLASGOW_HASKELL__ == 900 && __GLASGOW_HASKELL_PATCHLEVEL1__ == 1-    -- Cannot use fromIntegral because of https://gitlab.haskell.org/ghc/ghc/-/issues/19411-    fromIntegral' = fromInteger . toInteger-#else-    fromIntegral' = fromIntegral-#endif+    -- Cannot simply 'negate' followed by 'fromIntegral', because for bounded+    -- types 'negate minBound = minBound' and thus still negative.+    absAsNatural :: a -> Natural+    absAsNatural = fromInteger . Prelude.negate . toInteger {-# INLINABLE [1] (^%) #-}  {-# SPECIALISE [1] (^%) :: KnownNat m => Mod m -> Integer -> Mod m #-}
Data/Mod/Word.hs view
@@ -68,15 +68,7 @@ -- 1 -- -- __Note:__ 'Mod' 0 has no inhabitants, eventhough \( \mathbb{Z}/0\mathbb{Z} \) is technically isomorphic to \( \mathbb{Z} \).-newtype Mod (m :: Nat) = Mod-  { unMod :: Word-  -- ^ The canonical representative of the residue class,-  -- always between 0 and \( m - 1 \) (inclusively).-  ---  -- >>> :set -XDataKinds-  -- >>> -1 :: Mod 10-  -- 9-  }+newtype Mod (m :: Nat) = Mod Word   deriving (Eq, Ord, Generic)   deriving Storable   -- ^ No validation checks are performed;@@ -87,6 +79,15 @@   -- reading untrusted data may corrupt internal invariants. #endif +-- | The canonical representative of the residue class,+-- always between 0 and \( m - 1 \) (inclusively).+--+-- >>> :set -XDataKinds+-- >>> -1 :: Mod 10+-- 9+unMod :: Mod m -> Word+unMod (Mod w) = w+ instance NFData (Mod m)  instance Show (Mod m) where@@ -257,11 +258,11 @@   gcd (Mod !x) (Mod !y) = g     where       m = getModulus (natVal g)-      g = Mod $ if m > 1 then P.gcd (P.gcd m x) y else 0+      g = fromIntegral (P.gcd (P.gcd m x) y)   lcm (Mod !x) (Mod !y) = l     where       m = getModulus (natVal l)-      l = Mod $ if m > 1 then P.lcm (P.gcd m x) (P.gcd m y) else 0+      l = fromIntegral (P.lcm (P.gcd m x) (P.gcd m y))   coprime x y = Data.Euclidean.gcd x y == one  -- | 'Mod' @m@ is not even an@@ -434,9 +435,11 @@ (^%) :: (KnownNat m, Integral a) => Mod m -> a -> Mod m mx@(Mod !x) ^% a = case natVal mx of   NatJ#{} -> tooLargeModulus+  1 -> Mod 0   m@(NatS# _)     | a < 0 -> case invertMod mx of       Nothing      -> throw DivideByZero+      -- Somewhat accidentally this works even if a = minBound       Just (Mod y) -> Mod $ f y (-a) 1     | otherwise    -> Mod $ f x a 1     where
changelog.md view
@@ -1,3 +1,10 @@+# 0.2.2.0++* Plug a loophole to create `undefined {unMod = 100} :: Mod 1`.+* Fix `^%` when power is `minBound :: Int`.+* Fix `^%` when power is 0 and modulo is 1.+* Fix `Data.Euclidean.{gcd,lcm}` returning invalid inhabitants of `Mod m`.+ # 0.2.1.0  * Fix `invertMod (0 :: Mod 1)`.
mod.cabal view
@@ -1,5 +1,5 @@ name:          mod-version:       0.2.1.0+version:       0.2.2.0 cabal-version: >=1.10 build-type:    Simple license:       MIT@@ -15,7 +15,7 @@   Originally part of the <https://hackage.haskell.org/package/arithmoi arithmoi> package. category:      Math, Number Theory author:        Andrew Lelechenko <andrew.lelechenko@gmail.com>-tested-with:   GHC ==9.0.2 GHC ==9.2.8 GHC ==9.4.8 GHC ==9.6.7 GHC ==9.8.4 GHC ==9.10.2 GHC ==9.12.2 GHC ==9.14.1+tested-with:   GHC ==9.0.2 GHC ==9.2.8 GHC ==9.4.8 GHC ==9.6.7 GHC ==9.8.4 GHC ==9.10.3 GHC ==9.12.2 GHC ==9.14.1 extra-source-files:   changelog.md   README.md@@ -35,7 +35,7 @@  library   build-depends:-    base >=4.15 && <5,+    base >=4.15.1 && <5,     deepseq <1.6,     ghc-bignum <1.5   if flag(semirings)
test/Test.hs view
@@ -10,6 +10,7 @@  import Control.Exception (evaluate, try, ArithException(..)) import Data.Bits+import Data.Maybe (isNothing) import Data.Mod import qualified Data.Mod.Word as Word import Data.Proxy@@ -78,11 +79,15 @@     , testProperty "invertMod"   invertModRandomProp     , testProperty "powMod"      powModRandomProp     , testProperty "powMod on sum" powModRandomAdditiveProp-    , testProperty "powMod special case" powModCase+    , testProperty "powMod special case 1" powModCase1+    , testProperty "powMod special case 2" powModCase2+    , testProperty "powMod on minBound" powModMinBoundProp #ifdef MIN_VERSION_semirings     , testProperty "divide"  dividePropRandom-    , testProperty "gcd"     gcdIsPrincipalIdealRandom-    , testProperty "lcm"     lcmIsIntersectionOfIdealsRandom+    , testProperty "gcd is valid" gcdIsValid+    , testProperty "gcd is principal ideal" gcdIsPrincipalIdealRandom+    , testProperty "lcm is valid" lcmIsValid+    , testProperty "lcm is intersection of ideals" lcmIsIntersectionOfIdealsRandom     , testProperty "coprime" coprimeGeneratorsRandom     , testProperty "quotRem" quotRemPropRandom     , testProperty "degree"  degreePropRandom@@ -126,11 +131,15 @@     , testProperty "invertMod near maxBound" invertModWordRandomPropNearMaxBound     , testProperty "powMod"      powModWordRandomProp     , testProperty "powMod on sum" powModWordRandomAdditiveProp-    , testProperty "powMod special case" powModWordCase+    , testProperty "powMod special case 1" powModWordCase1+    , testProperty "powMod special case 2" powModWordCase2+    , testProperty "powMod on minBound" powModWordMinBoundProp #ifdef MIN_VERSION_semirings     , testProperty "divide"  divideWordPropRandom-    , testProperty "gcd"     gcdIsPrincipalIdealWordRandom-    , testProperty "lcm"     lcmIsIntersectionOfIdealsWordRandom+    , testProperty "gcd is valid" gcdIsValidWord+    , testProperty "gcd is principal ideal" gcdIsPrincipalIdealWordRandom+    , testProperty "lcm is valid" lcmIsValidWord+    , testProperty "lcm is intersection of ideals" lcmIsIntersectionOfIdealsWordRandom     , testProperty "coprime" coprimeGeneratorsWordRandom     , testProperty "quotRem" quotRemWordPropRandom     , testProperty "degree"  degreeWordPropRandom@@ -211,7 +220,7 @@ -- fromInteger  fromIntegerRandomProp :: Positive Integer -> Integer -> Property-fromIntegerRandomProp (Positive m) n = m > 1 ==> case someNatVal (fromInteger m) of+fromIntegerRandomProp (Positive m) n = m >= 1 ==> case someNatVal (fromInteger m) of   SomeNat p -> fromIntegerProp p n  fromIntegerProp :: forall m. KnownNat m => Proxy m -> Integer -> Property@@ -221,7 +230,7 @@     m = fromInteger n  fromIntegerWordRandomProp :: Word -> Integer -> Property-fromIntegerWordRandomProp m n = m > 1 ==> case someNatVal (fromIntegral m) of+fromIntegerWordRandomProp m n = m >= 1 ==> case someNatVal (fromIntegral m) of   SomeNat p -> fromIntegerWordProp p n  fromIntegerWordProp :: forall m. KnownNat m => Proxy m -> Integer -> Property@@ -234,7 +243,7 @@ -- invertMod  invertModRandomProp :: Positive Integer -> Integer -> Property-invertModRandomProp (Positive m) n = m > 1 ==> case someNatVal (fromInteger m) of+invertModRandomProp (Positive m) n = m >= 1 ==> case someNatVal (fromInteger m) of   SomeNat (Proxy :: Proxy m) -> invertModProp (fromInteger n :: Mod m)  invertModProp :: KnownNat m => Mod m -> Property@@ -245,7 +254,7 @@     g = gcd (unMod x) (fromIntegral (natVal x))  invertModWordRandomProp :: Word -> Integer -> Property-invertModWordRandomProp m n = m > 1 ==> case someNatVal (fromIntegral m) of+invertModWordRandomProp m n = m >= 1 ==> case someNatVal (fromIntegral m) of   SomeNat (Proxy :: Proxy m) -> invertModWordProp (fromInteger n :: Word.Mod m)  invertModWordRandomPropNearMaxBound :: Word -> Integer -> Property@@ -264,7 +273,7 @@ -- powMod  powModRandomProp :: Positive Integer -> Integer -> Int -> Property-powModRandomProp (Positive m) x n = m > 1 ==> case someNatVal (fromInteger m) of+powModRandomProp (Positive m) x n = m >= 1 ==> case someNatVal (fromInteger m) of   SomeNat (Proxy :: Proxy m) -> powModProp (fromInteger x :: Mod m) n  powModProp :: KnownNat m => Mod m -> Int -> Property@@ -275,23 +284,33 @@     Just x' -> x ^% n === getProduct (stimes (-n) (Product x'))  powModRandomAdditiveProp :: Positive Integer -> Integer -> Huge Integer -> Huge Integer -> Property-powModRandomAdditiveProp (Positive m) x (Huge n1) (Huge n2) = m > 1 ==> case someNatVal (fromInteger m) of+powModRandomAdditiveProp (Positive m) x (Huge n1) (Huge n2) = m >= 1 ==> case someNatVal (fromInteger m) of   SomeNat (Proxy :: Proxy m) -> powModAdditiveProp (fromInteger x :: Mod m) n1 n2  powModAdditiveProp :: KnownNat m => Mod m -> Integer -> Integer -> Property powModAdditiveProp x n1 n2-  | invertMod x == Nothing, n1 < 0 || n2 < 0+  | isNothing (invertMod x), n1 < 0 || n2 < 0   = property True   | otherwise   = (x ^% n1) * (x ^% n2) === x ^% (n1 + n2) -powModCase :: Property-powModCase = once $ 0 ^% n === (0 :: Mod 2)+powModCase1 :: Property+powModCase1 = once $ 0 ^% n === (0 :: Mod 2)   where     n = 1 `shiftL` 64 :: Integer +powModCase2 :: Property+powModCase2 = once $ 1 ^% 0 === (0 :: Mod 1)++powModMinBoundProp :: Positive Integer -> Integer -> Property+powModMinBoundProp (Positive m) x = case someNatVal (fromIntegral m) of+  SomeNat (Proxy :: Proxy m) -> let x' = fromInteger x :: Mod m in+    case invertMod x' of+      Nothing -> property True+      Just{} -> x' ^% (minBound :: Int) === x' ^% toInteger (minBound :: Int)+ powModWordRandomProp :: Word -> Integer -> Int -> Property-powModWordRandomProp m x k = m > 1 ==> case someNatVal (fromIntegral m) of+powModWordRandomProp m x k = m >= 1 ==> case someNatVal (fromIntegral m) of   SomeNat (Proxy :: Proxy m) -> powModWordProp (fromInteger x :: Word.Mod m) k  powModWordProp :: KnownNat m => Word.Mod m -> Int -> Property@@ -302,21 +321,31 @@     Just x' -> x Word.^% n === getProduct (stimes (-n) (Product x'))  powModWordRandomAdditiveProp :: Word -> Integer -> Huge Integer -> Huge Integer -> Property-powModWordRandomAdditiveProp m x (Huge n1) (Huge n2) = m > 1 ==> case someNatVal (fromIntegral m) of+powModWordRandomAdditiveProp m x (Huge n1) (Huge n2) = m >= 1 ==> case someNatVal (fromIntegral m) of   SomeNat (Proxy :: Proxy m) -> powModWordAdditiveProp (fromInteger x :: Word.Mod m) n1 n2  powModWordAdditiveProp :: KnownNat m => Word.Mod m -> Integer -> Integer -> Property powModWordAdditiveProp x n1 n2-  | Word.invertMod x == Nothing, n1 < 0 || n2 < 0+  | isNothing (Word.invertMod x), n1 < 0 || n2 < 0   = property True   | otherwise   = (x Word.^% n1) * (x Word.^% n2) === x Word.^% (n1 + n2) -powModWordCase :: Property-powModWordCase = once $ 0 Word.^% n === (0 :: Word.Mod 2)+powModWordCase1 :: Property+powModWordCase1 = once $ 0 Word.^% n === (0 :: Word.Mod 2)   where     n = 1 `shiftL` 64 :: Integer +powModWordCase2 :: Property+powModWordCase2 = once $ 1 Word.^% 0 === (0 :: Word.Mod 1)++powModWordMinBoundProp :: Word -> Integer -> Property+powModWordMinBoundProp m x = m >= 1 ==> case someNatVal (fromIntegral m) of+  SomeNat (Proxy :: Proxy m) -> let x' = fromInteger x :: Word.Mod m in+    case Word.invertMod x' of+      Nothing -> property True+      Just{} -> x' Word.^% (minBound :: Int) === x' Word.^% toInteger (minBound :: Int)+ newtype Huge a = Huge { _getHuge :: a }   deriving (Show) @@ -350,6 +379,14 @@   Just z -> x === y * z   Nothing -> filter ((== x) . (* y)) [minBound .. maxBound] === [] +gcdIsValid :: Positive Integer -> Integer -> Integer -> Property+gcdIsValid (Positive m) x y = case someNatVal (fromInteger m) of+  SomeNat (Proxy :: Proxy m) -> fromIntegral (unMod g) === g+    where+      x' = fromInteger x :: Mod m+      y' = fromInteger y :: Mod m+      g = E.gcd x' y'+ gcdIsPrincipalIdealRandom :: Positive (Small Integer) -> Integer -> Integer -> Property gcdIsPrincipalIdealRandom (Positive (Small m)) x y = case someNatVal (fromInteger m) of   SomeNat (Proxy :: Proxy m) -> gcdIsPrincipalIdeal (fromInteger x :: Mod m) (fromInteger y)@@ -360,6 +397,14 @@     genIdeal t = S.fromList $ map (* t) [minBound .. maxBound]     addIdeals us vs = S.fromList [ u + v | u <- S.toList us, v <- S.toList vs ] +lcmIsValid :: Positive Integer -> Integer -> Integer -> Property+lcmIsValid (Positive m) x y = case someNatVal (fromInteger m) of+  SomeNat (Proxy :: Proxy m) -> fromIntegral (unMod l) === l+    where+      x' = fromInteger x :: Mod m+      y' = fromInteger y :: Mod m+      l = E.lcm x' y'+ lcmIsIntersectionOfIdealsRandom :: Positive (Small Integer) -> Integer -> Integer -> Property lcmIsIntersectionOfIdealsRandom (Positive (Small m)) x y = case someNatVal (fromInteger m) of   SomeNat (Proxy :: Proxy m) -> lcmIsIntersectionOfIdeals (fromInteger x :: Mod m) (fromInteger y)@@ -410,6 +455,14 @@   Just z -> x === y * z   Nothing -> filter ((== x) . (* y)) [minBound .. maxBound] === [] +gcdIsValidWord :: Positive Word -> Integer -> Integer -> Property+gcdIsValidWord (Positive m) x y = case someNatVal (fromIntegral m) of+  SomeNat (Proxy :: Proxy m) -> fromIntegral (Word.unMod g) === g+    where+      x' = fromInteger x :: Word.Mod m+      y' = fromInteger y :: Word.Mod m+      g = E.gcd x' y'+ gcdIsPrincipalIdealWordRandom :: Positive Word -> Word -> Word -> Property gcdIsPrincipalIdealWordRandom (Positive m) x y = case someNatVal (fromIntegral m) of   SomeNat (Proxy :: Proxy m) -> gcdIsPrincipalIdealWord (fromIntegral x :: Word.Mod m) (fromIntegral y)@@ -419,6 +472,14 @@   where     genIdeal t = S.fromList $ map (* t) [minBound .. maxBound]     addIdeals us vs = S.fromList [ u + v | u <- S.toList us, v <- S.toList vs ]++lcmIsValidWord :: Positive Word -> Integer -> Integer -> Property+lcmIsValidWord (Positive m) x y = case someNatVal (fromIntegral m) of+  SomeNat (Proxy :: Proxy m) -> fromIntegral (Word.unMod l) === l+    where+      x' = fromInteger x :: Word.Mod m+      y' = fromInteger y :: Word.Mod m+      l = E.lcm x' y'  lcmIsIntersectionOfIdealsWordRandom :: Positive Word -> Word -> Word -> Property lcmIsIntersectionOfIdealsWordRandom (Positive m) x y = case someNatVal (fromIntegral m) of