packages feed

arithmoi 0.6.0.1 → 0.7.0.0

raw patch · 59 files changed

+2453/−436 lines, 59 filesdep +gaugedep +vectordep −criteriondep ~QuickCheckdep ~basedep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: gauge, vector

Dependencies removed: criterion

Dependency ranges changed: QuickCheck, base, containers, ghc-prim, tasty

API changes (from Hackage documentation)

- Math.NumberTheory.Powers.Integer: integerPower :: Integer -> Int -> Integer
- Math.NumberTheory.Powers.Integer: integerWordPower :: Integer -> Word -> Integer
- Math.NumberTheory.UniqueFactorisation: instance GHC.Classes.Eq Math.NumberTheory.UniqueFactorisation.BigPrime
- Math.NumberTheory.UniqueFactorisation: instance GHC.Classes.Eq Math.NumberTheory.UniqueFactorisation.SmallPrime
- Math.NumberTheory.UniqueFactorisation: instance GHC.Classes.Ord Math.NumberTheory.UniqueFactorisation.BigPrime
- Math.NumberTheory.UniqueFactorisation: instance GHC.Classes.Ord Math.NumberTheory.UniqueFactorisation.SmallPrime
- Math.NumberTheory.UniqueFactorisation: instance GHC.Show.Show Math.NumberTheory.UniqueFactorisation.BigPrime
- Math.NumberTheory.UniqueFactorisation: instance GHC.Show.Show Math.NumberTheory.UniqueFactorisation.SmallPrime
+ Math.NumberTheory.ArithmeticFunctions: MoebiusN :: Moebius
+ Math.NumberTheory.ArithmeticFunctions: MoebiusP :: Moebius
+ Math.NumberTheory.ArithmeticFunctions: MoebiusZ :: Moebius
+ Math.NumberTheory.ArithmeticFunctions: data Moebius
+ Math.NumberTheory.ArithmeticFunctions: runMoebius :: Num a => Moebius -> a
+ Math.NumberTheory.ArithmeticFunctions.Mertens: mertens :: Word -> Int
+ Math.NumberTheory.ArithmeticFunctions.Moebius: MoebiusN :: Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: MoebiusP :: Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: MoebiusZ :: Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: data Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance Data.Semigroup.Semigroup Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance Data.Vector.Generic.Mutable.Base.MVector Data.Vector.Unboxed.Base.MVector Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance Data.Vector.Unboxed.Base.Unbox Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance GHC.Base.Monoid Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance GHC.Classes.Eq Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance GHC.Classes.Ord Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: instance GHC.Show.Show Math.NumberTheory.ArithmeticFunctions.Moebius.Moebius
+ Math.NumberTheory.ArithmeticFunctions.Moebius: runMoebius :: Num a => Moebius -> a
+ Math.NumberTheory.ArithmeticFunctions.Moebius: sieveBlockMoebius :: Word -> Word -> Vector Moebius
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: SieveBlockConfig :: a -> (Word -> Word -> a) -> (a -> a -> a) -> SieveBlockConfig a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: [sbcAppend] :: SieveBlockConfig a -> a -> a -> a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: [sbcEmpty] :: SieveBlockConfig a -> a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: [sbcFunctionOnPrimePower] :: SieveBlockConfig a -> Word -> Word -> a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: additiveSieveBlockConfig :: Num a => (Word -> Word -> a) -> SieveBlockConfig a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: data SieveBlockConfig a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: multiplicativeSieveBlockConfig :: Num a => (Word -> Word -> a) -> SieveBlockConfig a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: runFunctionOverBlock :: ArithmeticFunction Word a -> Word -> Word -> Vector a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: sieveBlock :: SieveBlockConfig a -> Word -> Word -> Vector a
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: sieveBlockMoebius :: Word -> Word -> Vector Moebius
+ Math.NumberTheory.ArithmeticFunctions.SieveBlock: sieveBlockUnboxed :: Unbox a => SieveBlockConfig a -> Word -> Word -> Vector a
+ Math.NumberTheory.GCD: splitIntoCoprimes :: (Integral a, Eq b, Num b) => [(a, b)] -> [(a, b)]
+ Math.NumberTheory.Moduli.Class: instance GHC.Enum.Enum (Math.NumberTheory.Moduli.Class.Mod m)
+ Math.NumberTheory.Moduli.Class: instance GHC.TypeNats.KnownNat m => GHC.Enum.Bounded (Math.NumberTheory.Moduli.Class.Mod m)
+ Math.NumberTheory.Moduli.PrimitiveRoot: CG2 :: CyclicGroup a
+ Math.NumberTheory.Moduli.PrimitiveRoot: CG4 :: CyclicGroup a
+ Math.NumberTheory.Moduli.PrimitiveRoot: CGDoubleOddPrimePower :: (Prime a) -> Word -> CyclicGroup a
+ Math.NumberTheory.Moduli.PrimitiveRoot: CGOddPrimePower :: (Prime a) -> Word -> CyclicGroup a
+ Math.NumberTheory.Moduli.PrimitiveRoot: cyclicGroupFromModulo :: (Ord a, Integral a, UniqueFactorisation a) => a -> Maybe (CyclicGroup a)
+ Math.NumberTheory.Moduli.PrimitiveRoot: cyclicGroupToModulo :: (Integral a, UniqueFactorisation a) => CyclicGroup a -> Prefactored a
+ Math.NumberTheory.Moduli.PrimitiveRoot: data CyclicGroup a
+ Math.NumberTheory.Moduli.PrimitiveRoot: instance GHC.Show.Show (Math.NumberTheory.Primes.Types.Prime a) => GHC.Show.Show (Math.NumberTheory.Moduli.PrimitiveRoot.CyclicGroup a)
+ Math.NumberTheory.Moduli.PrimitiveRoot: isPrimitiveRoot :: KnownNat n => Mod n -> Bool
+ Math.NumberTheory.Moduli.PrimitiveRoot: isPrimitiveRoot' :: (Integral a, UniqueFactorisation a) => CyclicGroup a -> a -> Bool
+ Math.NumberTheory.Powers: powMod :: (Integral a, Integral b) => a -> b -> a -> a
+ Math.NumberTheory.Powers.Modular: powMod :: (Integral a, Integral b) => a -> b -> a -> a
+ Math.NumberTheory.Powers.Modular: powModInt :: Int -> Int -> Int -> Int
+ Math.NumberTheory.Powers.Modular: powModWord :: Word -> Word -> Word -> Word
+ Math.NumberTheory.Prefactored: data Prefactored a
+ Math.NumberTheory.Prefactored: fromFactors :: Integral a => [(a, Word)] -> Prefactored a
+ Math.NumberTheory.Prefactored: fromValue :: a -> Prefactored a
+ Math.NumberTheory.Prefactored: instance (GHC.Real.Integral a, Math.NumberTheory.UniqueFactorisation.UniqueFactorisation a) => GHC.Num.Num (Math.NumberTheory.Prefactored.Prefactored a)
+ Math.NumberTheory.Prefactored: instance GHC.Show.Show a => GHC.Show.Show (Math.NumberTheory.Prefactored.Prefactored a)
+ Math.NumberTheory.Prefactored: instance Math.NumberTheory.UniqueFactorisation.UniqueFactorisation a => Math.NumberTheory.UniqueFactorisation.UniqueFactorisation (Math.NumberTheory.Prefactored.Prefactored a)
+ Math.NumberTheory.SmoothNumbers: data SmoothBasis a
+ Math.NumberTheory.SmoothNumbers: fromList :: Integral a => [a] -> Maybe (SmoothBasis a)
+ Math.NumberTheory.SmoothNumbers: fromSet :: Integral a => Set a -> Maybe (SmoothBasis a)
+ Math.NumberTheory.SmoothNumbers: fromSmoothUpperBound :: Integral a => a -> Maybe (SmoothBasis a)
+ Math.NumberTheory.SmoothNumbers: instance GHC.Show.Show a => GHC.Show.Show (Math.NumberTheory.SmoothNumbers.SmoothBasis a)
+ Math.NumberTheory.SmoothNumbers: smoothOver :: Integral a => SmoothBasis a -> [a]
+ Math.NumberTheory.SmoothNumbers: smoothOverInRange :: Integral a => SmoothBasis a -> a -> a -> [a]
+ Math.NumberTheory.SmoothNumbers: smoothOverInRangeBF :: Integral a => SmoothBasis a -> a -> a -> [a]
+ Math.NumberTheory.UniqueFactorisation: isPrime :: UniqueFactorisation a => a -> Maybe (Prime a)
- Math.NumberTheory.ArithmeticFunctions: jordan :: (UniqueFactorisation n, Integral n) => Word -> n -> n
+ Math.NumberTheory.ArithmeticFunctions: jordan :: (UniqueFactorisation n, Num n) => Word -> n -> n
- Math.NumberTheory.ArithmeticFunctions: jordanA :: forall n. (UniqueFactorisation n, Integral n) => Word -> ArithmeticFunction n n
+ Math.NumberTheory.ArithmeticFunctions: jordanA :: forall n. (UniqueFactorisation n, Num n) => Word -> ArithmeticFunction n n
- Math.NumberTheory.ArithmeticFunctions: moebius :: (UniqueFactorisation n, Num a) => n -> a
+ Math.NumberTheory.ArithmeticFunctions: moebius :: UniqueFactorisation n => n -> Moebius
- Math.NumberTheory.ArithmeticFunctions: moebiusA :: Num a => ArithmeticFunction n a
+ Math.NumberTheory.ArithmeticFunctions: moebiusA :: ArithmeticFunction n Moebius
- Math.NumberTheory.ArithmeticFunctions: totient :: (UniqueFactorisation n, Integral n) => n -> n
+ Math.NumberTheory.ArithmeticFunctions: totient :: (UniqueFactorisation n, Num n) => n -> n
- Math.NumberTheory.ArithmeticFunctions: totientA :: forall n. (UniqueFactorisation n, Integral n) => ArithmeticFunction n n
+ Math.NumberTheory.ArithmeticFunctions: totientA :: forall n. (UniqueFactorisation n, Num n) => ArithmeticFunction n n
- Math.NumberTheory.Primes.Factorisation: curveFactorisation :: Maybe Integer -> (Integer -> Bool) -> (Integer -> g -> (Integer, g)) -> g -> Maybe Int -> Integer -> [(Integer, Int)]
+ Math.NumberTheory.Primes.Factorisation: curveFactorisation :: forall g. Maybe Integer -> (Integer -> Bool) -> (Integer -> g -> (Integer, g)) -> g -> Maybe Int -> Integer -> [(Integer, Int)]

Files

Changes view
@@ -1,3 +1,45 @@+0.7.0.0+    This release supports GHC 7.8, 7.10, 8.0, 8.2 and 8.4.++    Breaking changes:++        Remove 'Math.NumberTheory.Powers.Integer', deprecated in 0.5.0.0.++        Deprecate 'Math.NumberTheory.Primes.Heap'.+        Use 'Math.NumberTheory.Primes.Sieve' instead.++        Deprecate 'FactorSieve', 'TotientSieve', 'CarmichaelSieve' and+        accompanying functions. Use new general approach for bulk evaluation+        of arithmetic functions instead (#77).++        Now 'moebius' returns not a number, but a value of 'Moebius' type (#90).++    New functions:++        A general framework for bulk evaluation of arithmetic functions (#77):++        > runFunctionOverBlock carmichaelA 1 10+        [1,1,2,2,4,2,6,2,6,4]++        Implement a sublinear algorithm for Mertens function (#90):++        > map (mertens . (10 ^)) [0..9]+        [1,-1,1,2,-23,-48,212,1037,1928,-222]++        Add basic support for cyclic groups and primitive roots (#86).++        Implement an efficient modular exponentiation (#86).++        Write routines for lazy generation of smooth numbers (#91).++        > smoothOverInRange (fromJust (fromList [3,5,7])) 1000 2000+        [1029,1125,1215,1225,1323,1575,1701,1715,1875]++    Improvements:++        Now factorisation of large integers and Gaussian integers produces+        factors as lazy as possible (#72, #76).+ 0.6.0.1:     Switch to smallcheck 1.1.3. @@ -96,7 +138,7 @@     Add new cabal flag check-bounds, which replaces all unsafe array functions with safe ones.      Add basic functions on Gaussian integers.-    Add Moebius mu-function.+    Add Möbius mu-function.      Forbid non-positive moduli in Math.NumberTheory.Moduli. @@ -123,7 +165,7 @@ 0.4.0.1:     Fixed Haddock bug 0.4.0.0:-    Added generalised Moebius inversion, to be continued+    Added generalised Möbius inversion, to be continued 0.3.0.0:     Added modular square roots and Chinese remainder theorem 0.2.0.6:
GHC/TypeNats/Compat.hs view
@@ -1,15 +1,16 @@ {-# LANGUAGE CPP         #-}  {-# OPTIONS_HADDOCK hide #-}- #if MIN_VERSION_base(4,10,0)- module GHC.TypeNats.Compat   ( module GHC.TypeNats   ) where +#if MIN_VERSION_base(4,11,0)+import GHC.TypeNats hiding (Mod)+#else import GHC.TypeNats-+#endif #else  module GHC.TypeNats.Compat
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2011 Daniel Fischer, 2016-2017 Andrew Lelechenko, Carter Schonwald+Copyright (c) 2011 Daniel Fischer, 2016-2017 Andrew Lelechenko, Carter Schonwald, Google Inc.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and  associated documentation files (the "Software"), to deal in the Software without restriction,
Math/NumberTheory/ArithmeticFunctions/Class.hs view
@@ -21,7 +21,9 @@   ) where  import Control.Applicative+#if __GLASGOW_HASKELL__ < 803 import Data.Semigroup+#endif  #if MIN_VERSION_base(4,8,0) #else
+ Math/NumberTheory/ArithmeticFunctions/Mertens.hs view
@@ -0,0 +1,78 @@+-- |+-- Module:      Math.NumberTheory.ArithmeticFunctions.Mertens+-- Copyright:   (c) 2018 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Values of <https://en.wikipedia.org/wiki/Mertens_function Mertens function>.+--++{-# LANGUAGE CPP        #-}+{-# LANGUAGE LambdaCase #-}++module Math.NumberTheory.ArithmeticFunctions.Mertens+  ( mertens+  ) where++import qualified Data.Vector.Unboxed as U+#if __GLASGOW_HASKELL__ < 709+import Data.Word+#endif++import Math.NumberTheory.Powers.Cubes+import Math.NumberTheory.Powers.Squares+import Math.NumberTheory.ArithmeticFunctions.Moebius++-- | Compute individual values of Mertens function in O(n^(2/3)) time and space.+--+-- > > map (mertens . (10 ^)) [0..9]+-- > [1,-1,1,2,-23,-48,212,1037,1928,-222]+--+-- The implementation follows Theorem 3.1 from <https://arxiv.org/pdf/1610.08551.pdf Computations of the Mertens function and improved bounds on the Mertens conjecture> by G. Hurst, excluding segmentation of sieves.+mertens :: Word -> Int+mertens 0 = 0+mertens 1 = 1+mertens x = sumMultMoebius lookupMus (\n -> sfunc (x `quot` n)) [1 .. x `quot` u]+  where+    u = (integerSquareRoot x + 1) `max` ((integerCubeRoot x) ^ (2 :: Word) `quot` 2)++    sfunc :: Word -> Int+    sfunc y+      = 1+      - sum [ U.unsafeIndex mes (fromIntegral $ y `quot` n) |  n <- [y `quot` u + 1 .. kappa] ]+      + fromIntegral kappa * U.unsafeIndex mes (fromIntegral nu)+      - sumMultMoebius lookupMus (\n -> fromIntegral $ y `quot` n) [1 .. nu]+      where+        nu = integerSquareRoot y+        kappa = y `quot` (nu + 1)++    -- cacheSize ~ u+    cacheSize :: Word+    cacheSize = u `max` (x `quot` u) `max` integerSquareRoot x++    -- 1-based index+    mus :: U.Vector Moebius+    mus = sieveBlockMoebius 1 cacheSize++    lookupMus :: Word -> Moebius+    lookupMus i = U.unsafeIndex mus (fromIntegral (i - 1))++    -- 0-based index+    mes :: U.Vector Int+    mes = U.scanl' go 0 mus+      where+        go acc = \case+          MoebiusN -> acc - 1+          MoebiusZ -> acc+          MoebiusP -> acc + 1++-- | Compute sum (map (\x -> runMoebius (mu x) * f x))+sumMultMoebius :: (Word -> Moebius) -> (Word -> Int) -> [Word] -> Int+sumMultMoebius mu f = foldl go 0+  where+    go acc i = case mu i of+      MoebiusN -> acc - f i+      MoebiusZ -> acc+      MoebiusP -> acc + f i
+ Math/NumberTheory/ArithmeticFunctions/Moebius.hs view
@@ -0,0 +1,176 @@+-- |+-- Module:      Math.NumberTheory.ArithmeticFunctions.Moebius+-- Copyright:   (c) 2018 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Values of <https://en.wikipedia.org/wiki/Möbius_function Möbius function>.+--++{-# LANGUAGE BangPatterns          #-}+{-# LANGUAGE CPP                   #-}+{-# LANGUAGE MagicHash             #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies          #-}++module Math.NumberTheory.ArithmeticFunctions.Moebius+  ( Moebius(..)+  , runMoebius+  , sieveBlockMoebius+  ) where++import Control.Monad (forM_, liftM)+import Control.Monad.ST (runST)+import Data.Bits+import Data.Int+import Data.Word+#if __GLASGOW_HASKELL__ < 803+import Data.Semigroup+#endif+import qualified Data.Vector.Generic         as G+import qualified Data.Vector.Generic.Mutable as M+import qualified Data.Vector.Primitive as P+import qualified Data.Vector.Unboxed         as U+import qualified Data.Vector.Unboxed.Mutable as MU+import GHC.Exts+import GHC.Integer.GMP.Internals+import Unsafe.Coerce++import Math.NumberTheory.Primes (primes)+import Math.NumberTheory.Powers.Squares (integerSquareRoot)+import Math.NumberTheory.Utils.FromIntegral (wordToInt)++import Math.NumberTheory.Logarithms++-- | Represents three possible values of <https://en.wikipedia.org/wiki/Möbius_function Möbius function>.+data Moebius+  = MoebiusN -- ^ −1+  | MoebiusZ -- ^  0+  | MoebiusP -- ^  1+  deriving (Eq, Ord, Show)++-- | Convert to any numeric type.+runMoebius :: Num a => Moebius -> a+runMoebius m = fromInteger (S# (dataToTag# m -# 1#))++fromMoebius :: Moebius -> Int8+fromMoebius m = fromIntegral $ I# (dataToTag# m)+{-# INLINE fromMoebius #-}++toMoebius :: Int8 -> Moebius+toMoebius i = let !(I# i#) = fromIntegral i in tagToEnum# i#+{-# INLINE toMoebius #-}++newtype instance U.MVector s Moebius = MV_Moebius (P.MVector s Int8)+newtype instance U.Vector    Moebius = V_Moebius  (P.Vector    Int8)++instance U.Unbox Moebius++instance M.MVector U.MVector Moebius where+  {-# INLINE basicLength #-}+  {-# INLINE basicUnsafeSlice #-}+  {-# INLINE basicOverlaps #-}+  {-# INLINE basicUnsafeNew #-}+  {-# INLINE basicInitialize #-}+  {-# INLINE basicUnsafeReplicate #-}+  {-# INLINE basicUnsafeRead #-}+  {-# INLINE basicUnsafeWrite #-}+  {-# INLINE basicClear #-}+  {-# INLINE basicSet #-}+  {-# INLINE basicUnsafeCopy #-}+  {-# INLINE basicUnsafeGrow #-}+  basicLength (MV_Moebius v) = M.basicLength v+  basicUnsafeSlice i n (MV_Moebius v) = MV_Moebius $ M.basicUnsafeSlice i n v+  basicOverlaps (MV_Moebius v1) (MV_Moebius v2) = M.basicOverlaps v1 v2+  basicUnsafeNew n = MV_Moebius `liftM` M.basicUnsafeNew n+  basicInitialize (MV_Moebius v) = M.basicInitialize v+  basicUnsafeReplicate n x = MV_Moebius `liftM` M.basicUnsafeReplicate n (fromMoebius x)+  basicUnsafeRead (MV_Moebius v) i = toMoebius `liftM` M.basicUnsafeRead v i+  basicUnsafeWrite (MV_Moebius v) i x = M.basicUnsafeWrite v i (fromMoebius x)+  basicClear (MV_Moebius v) = M.basicClear v+  basicSet (MV_Moebius v) x = M.basicSet v (fromMoebius x)+  basicUnsafeCopy (MV_Moebius v1) (MV_Moebius v2) = M.basicUnsafeCopy v1 v2+  basicUnsafeMove (MV_Moebius v1) (MV_Moebius v2) = M.basicUnsafeMove v1 v2+  basicUnsafeGrow (MV_Moebius v) n = MV_Moebius `liftM` M.basicUnsafeGrow v n++instance G.Vector U.Vector Moebius where+  {-# INLINE basicUnsafeFreeze #-}+  {-# INLINE basicUnsafeThaw #-}+  {-# INLINE basicLength #-}+  {-# INLINE basicUnsafeSlice #-}+  {-# INLINE basicUnsafeIndexM #-}+  {-# INLINE elemseq #-}+  basicUnsafeFreeze (MV_Moebius v) = V_Moebius `liftM` G.basicUnsafeFreeze v+  basicUnsafeThaw (V_Moebius v) = MV_Moebius `liftM` G.basicUnsafeThaw v+  basicLength (V_Moebius v) = G.basicLength v+  basicUnsafeSlice i n (V_Moebius v) = V_Moebius $ G.basicUnsafeSlice i n v+  basicUnsafeIndexM (V_Moebius v) i = toMoebius `liftM` G.basicUnsafeIndexM v i+  basicUnsafeCopy (MV_Moebius mv) (V_Moebius v) = G.basicUnsafeCopy mv v+  elemseq _ = seq++instance Semigroup Moebius where+  MoebiusZ <> _ = MoebiusZ+  _ <> MoebiusZ = MoebiusZ+  MoebiusP <> a = a+  a <> MoebiusP = a+  _ <> _ = MoebiusP++instance Monoid Moebius where+  mempty  = MoebiusP+  mappend = (<>)++-- | Evaluate the Möbius function over a block.+-- Value of @f@ at 0, if zero falls into block, is undefined.+--+-- Based on the sieving algorithm from p. 3 of <https://arxiv.org/pdf/1610.08551.pdf Computations of the Mertens function and improved bounds on the Mertens conjecture> by G. Hurst. It is approximately 5x faster than 'Math.NumberTheory.ArithmeticFunctions.SieveBlock.sieveBlockUnboxed'.+--+-- > > sieveBlockMoebius 1 10+-- > [MoebiusP, MoebiusN, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusN, MoebiusZ, MoebiusZ, MoebiusP]+sieveBlockMoebius+  :: Word+  -> Word+  -> U.Vector Moebius+sieveBlockMoebius _ 0 = U.empty+sieveBlockMoebius lowIndex' len'+  = (unsafeCoerce :: U.Vector Word8 -> U.Vector Moebius) $ runST $ do+    as <- MU.replicate len (0x80 :: Word8)+    forM_ ps $ \p -> do+      let offset  = negate lowIndex `mod` p+          offset2 = negate lowIndex `mod` (p * p)+          l :: Word8+          l = fromIntegral $ intLog2 p .|. 1+      forM_ [offset, offset + p .. len - 1] $ \ix -> do+        MU.unsafeModify as (\y -> y + l) ix+      forM_ [offset2, offset2 + p * p .. len - 1] $ \ix -> do+        MU.unsafeWrite as ix 0+    forM_ [0 .. len - 1] $ \ix -> do+      MU.unsafeModify as (mapper ix) ix+    U.unsafeFreeze as++  where+    lowIndex :: Int+    lowIndex = wordToInt lowIndex'++    len :: Int+    len = wordToInt len'++    highIndex :: Int+    highIndex = lowIndex + len - 1++    -- Bit fiddling in 'mapper' is correct only+    -- if all sufficiently small (<= 191) primes has been sieved out.+    ps :: [Int]+    ps = takeWhile (<= (191 `max` integerSquareRoot highIndex)) $ map fromInteger primes++    mapper :: Int -> Word8 -> Word8+    mapper ix val+      | val .&. 0x80 == 0x00+      = 1+      | fromIntegral (val .&. 0x7F) < intLog2 (ix + lowIndex) - 5+        - (if ix + lowIndex >= 0x100000   then 2 else 0)+        - (if ix + lowIndex >= 0x10000000 then 1 else 0)+      = (val .&. 1) `shiftL` 1+      | otherwise+      = ((val .&. 1) `xor` 1) `shiftL` 1
+ Math/NumberTheory/ArithmeticFunctions/SieveBlock.hs view
@@ -0,0 +1,132 @@+-- |+-- Module:      Math.NumberTheory.ArithmeticFunctions.SieveBlock+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Bulk evaluation of arithmetic functions over continuous intervals+-- without factorisation.+--++{-# LANGUAGE BangPatterns        #-}+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE MagicHash           #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UnboxedTuples       #-}++module Math.NumberTheory.ArithmeticFunctions.SieveBlock+  ( runFunctionOverBlock+  , SieveBlockConfig(..)+  , multiplicativeSieveBlockConfig+  , additiveSieveBlockConfig+  , sieveBlock+  , sieveBlockUnboxed+  , sieveBlockMoebius+  ) where++import Control.Monad (forM_)+import Control.Monad.ST (runST)+import Data.Coerce+import qualified Data.Vector as V+import qualified Data.Vector.Mutable as MV+import GHC.Exts+#if __GLASGOW_HASKELL__ < 709+import Data.Monoid+#endif++import Math.NumberTheory.ArithmeticFunctions.Class+import Math.NumberTheory.ArithmeticFunctions.Moebius (sieveBlockMoebius)+import Math.NumberTheory.ArithmeticFunctions.SieveBlock.Unboxed+import Math.NumberTheory.Logarithms (integerLogBase')+import Math.NumberTheory.Primes (primes)+import Math.NumberTheory.Primes.Types+import Math.NumberTheory.Powers.Squares (integerSquareRoot)+import Math.NumberTheory.Utils (splitOff#)+import Math.NumberTheory.Utils.FromIntegral (wordToInt, intToWord)++-- | 'runFunctionOverBlock' @f@ @x@ @l@ evaluates an arithmetic function+-- for integers between @x@ and @x+l-1@ and returns a vector of length @l@.+-- It completely avoids factorisation, so it is asymptotically faster than+-- pointwise evaluation of @f@.+--+-- Value of @f@ at 0, if zero falls into block, is undefined.+--+-- Beware that for underlying non-commutative monoids the results may potentially+-- differ from pointwise application via 'runFunction'.+--+-- This is a thin wrapper over 'sieveBlock', read more details there.+--+-- > > runFunctionOverBlock carmichaelA 1 10+-- > [1,1,2,2,4,2,6,2,6,4]+runFunctionOverBlock+  :: ArithmeticFunction Word a+  -> Word+  -> Word+  -> V.Vector a+runFunctionOverBlock (ArithmeticFunction f g) = (V.map g .) . sieveBlock SieveBlockConfig+  { sbcEmpty                = mempty+  , sbcAppend               = mappend+  , sbcFunctionOnPrimePower = coerce f+  }++-- | Evaluate a function over a block in accordance to provided configuration.+-- Value of @f@ at 0, if zero falls into block, is undefined.+--+-- Based on Algorithm M of <https://arxiv.org/pdf/1305.1639.pdf Parity of the number of primes in a given interval and algorithms of the sublinear summation> by A. V. Lelechenko. See Lemma 2 on p. 5 on its algorithmic complexity. For the majority of use-cases its time complexity is O(x^(1+ε)).+--+-- 'sieveBlock' is similar to 'sieveBlockUnboxed' up to flavour of 'Data.Vector',+-- but is typically 7x-10x slower and consumes 3x memory.+-- Use unboxed version whenever possible.+--+-- For example, following code lists smallest prime factors:+--+-- > > sieveBlock (SieveBlockConfig maxBound min (\p _ -> p) 2 10)+-- > [2,3,2,5,2,7,2,3,2,11]+sieveBlock+  :: SieveBlockConfig a+  -> Word+  -> Word+  -> V.Vector a+sieveBlock _ _ 0 = V.empty+sieveBlock (SieveBlockConfig empty f append) lowIndex' len' = runST $ do++    let lowIndex :: Int+        lowIndex = wordToInt lowIndex'++        len :: Int+        len = wordToInt len'++    as <- V.unsafeThaw $ V.enumFromN lowIndex' len+    bs <- MV.replicate len empty++    let highIndex :: Int+        highIndex = lowIndex + len - 1++        ps :: [Int]+        ps = takeWhile (<= integerSquareRoot highIndex) $ map fromInteger primes++    forM_ ps $ \p -> do++      let p# :: Word#+          !p'@(W# p#) = intToWord p++          fs = V.generate+            (integerLogBase' (toInteger p) (toInteger highIndex))+            (\k -> f p' (intToWord k + 1))++          offset :: Int+          offset = negate lowIndex `mod` p++      forM_ [offset, offset + p .. len - 1] $ \ix -> do+        W# a# <- MV.unsafeRead as ix+        let !(# pow#, a'# #) = splitOff# p# (a# `quotWord#` p#)+        MV.unsafeWrite as ix (W# a'#)+        MV.unsafeModify bs (\y -> y `append` V.unsafeIndex fs (I# pow#)) ix++    forM_ [0 .. len - 1] $ \k -> do+      a <- MV.unsafeRead as k+      MV.unsafeModify bs (\b -> if a /= 1 then b `append` f a 1 else b) k++    V.unsafeFreeze bs
+ Math/NumberTheory/ArithmeticFunctions/SieveBlock/Unboxed.hs view
@@ -0,0 +1,132 @@+-- |+-- Module:      Math.NumberTheory.ArithmeticFunctions.SieveBlock.Unboxed+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Bulk evaluation of arithmetic functions without factorisation+-- of arguments.+--++{-# LANGUAGE BangPatterns        #-}+{-# LANGUAGE MagicHash           #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UnboxedTuples       #-}++module Math.NumberTheory.ArithmeticFunctions.SieveBlock.Unboxed+  ( SieveBlockConfig(..)+  , multiplicativeSieveBlockConfig+  , additiveSieveBlockConfig+  , sieveBlockUnboxed+  ) where++import Control.Monad (forM_)+import Control.Monad.ST (runST)+import qualified Data.Vector.Unboxed as V+import qualified Data.Vector.Unboxed.Mutable as MV+import GHC.Exts++import Math.NumberTheory.ArithmeticFunctions.Moebius (Moebius)+import Math.NumberTheory.Logarithms (integerLogBase')+import Math.NumberTheory.Primes (primes)+import Math.NumberTheory.Powers.Squares (integerSquareRoot)+import Math.NumberTheory.Utils (splitOff#)+import Math.NumberTheory.Utils.FromIntegral (wordToInt, intToWord)++-- | A record, which specifies a function to evaluate over a block.+--+-- For example, here is a configuration for the totient function:+--+-- > SieveBlockConfig+-- >   { sbcEmpty                = 1+-- >   , sbcFunctionOnPrimePower = (\p a -> (p - 1) * p ^ (a - 1)+-- >   , sbcAppend               = (*)+-- >   }+data SieveBlockConfig a = SieveBlockConfig+  { sbcEmpty                :: a+    -- ^ value of a function on 1+  , sbcFunctionOnPrimePower :: Word -> Word -> a+    -- ^ how to evaluate a function on prime powers+  , sbcAppend               :: a -> a -> a+    -- ^ how to combine values of a function on coprime arguments+  }++-- | Create a config for a multiplicative function from its definition on prime powers.+multiplicativeSieveBlockConfig :: Num a => (Word -> Word -> a) -> SieveBlockConfig a+multiplicativeSieveBlockConfig f = SieveBlockConfig+  { sbcEmpty                = 1+  , sbcFunctionOnPrimePower = f+  , sbcAppend               = (*)+  }++-- | Create a config for an additive function from its definition on prime powers.+additiveSieveBlockConfig :: Num a => (Word -> Word -> a) -> SieveBlockConfig a+additiveSieveBlockConfig f = SieveBlockConfig+  { sbcEmpty                = 0+  , sbcFunctionOnPrimePower = f+  , sbcAppend               = (+)+  }++-- | Evaluate a function over a block in accordance to provided configuration.+-- Value of @f@ at 0, if zero falls into block, is undefined.+--+-- Based on Algorithm M of <https://arxiv.org/pdf/1305.1639.pdf Parity of the number of primes in a given interval and algorithms of the sublinear summation> by A. V. Lelechenko. See Lemma 2 on p. 5 on its algorithmic complexity. For the majority of use-cases its time complexity is O(x^(1+ε)).+--+-- For example, here is an analogue of divisor function 'tau':+--+-- > > sieveBlockUnboxed (SieveBlockConfig 1 (*) (\_ a -> a + 1) 1 10)+-- > [1,2,2,3,2,4,2,4,3,4]+sieveBlockUnboxed+  :: V.Unbox a+  => SieveBlockConfig a+  -> Word+  -> Word+  -> V.Vector a+sieveBlockUnboxed _ _ 0 = V.empty+sieveBlockUnboxed (SieveBlockConfig empty f append) lowIndex' len' = runST $ do++    let lowIndex :: Int+        lowIndex = wordToInt lowIndex'++        len :: Int+        len = wordToInt len'++    as <- V.unsafeThaw $ V.enumFromN lowIndex' len+    bs <- MV.replicate len empty++    let highIndex :: Int+        highIndex = lowIndex + len - 1++        ps :: [Int]+        ps = takeWhile (<= integerSquareRoot highIndex) $ map fromInteger primes++    forM_ ps $ \p -> do++      let p# :: Word#+          !p'@(W# p#) = intToWord p++          fs = V.generate+            (integerLogBase' (toInteger p) (toInteger highIndex))+            (\k -> f p' (intToWord k + 1))++          offset :: Int+          offset = negate lowIndex `mod` p++      forM_ [offset, offset + p .. len - 1] $ \ix -> do+        W# a# <- MV.unsafeRead as ix+        let !(# pow#, a'# #) = splitOff# p# (a# `quotWord#` p#)+        MV.unsafeWrite as ix (W# a'#)+        MV.unsafeModify bs (\y -> y `append` V.unsafeIndex fs (I# pow#)) ix++    forM_ [0 .. len - 1] $ \k -> do+      a <- MV.unsafeRead as k+      MV.unsafeModify bs (\b -> if a /= 1 then b `append` f a 1 else b) k++    V.unsafeFreeze bs++{-# SPECIALIZE sieveBlockUnboxed :: SieveBlockConfig Int  -> Word -> Word -> V.Vector Int  #-}+{-# SPECIALIZE sieveBlockUnboxed :: SieveBlockConfig Word -> Word -> Word -> V.Vector Word #-}+{-# SPECIALIZE sieveBlockUnboxed :: SieveBlockConfig Bool -> Word -> Word -> V.Vector Bool #-}+{-# SPECIALIZE sieveBlockUnboxed :: SieveBlockConfig Moebius -> Word -> Word -> V.Vector Moebius #-}
Math/NumberTheory/ArithmeticFunctions/Standard.hs view
@@ -26,7 +26,7 @@   , sigma, sigmaA   , totient, totientA   , jordan, jordanA-  , moebius, moebiusA+  , moebius, moebiusA, Moebius(..), runMoebius   , liouville, liouvilleA     -- * Additive functions   , additive@@ -45,7 +45,9 @@ import Data.Semigroup  import Math.NumberTheory.ArithmeticFunctions.Class+import Math.NumberTheory.ArithmeticFunctions.Moebius import Math.NumberTheory.UniqueFactorisation+import Math.NumberTheory.Utils.FromIntegral  import Numeric.Natural @@ -55,9 +57,6 @@ import Data.Word #endif -wordToInt :: Word -> Int-wordToInt = fromIntegral- -- | Create a multiplicative function from the function on prime's powers. See examples below. multiplicative :: Num a => (Prime n -> Word -> a) -> ArithmeticFunction n a multiplicative f = ArithmeticFunction ((Product .) . f) getProduct@@ -130,38 +129,38 @@ sigmaHelper pa k = (pa ^ wordToInt (k + 1) - 1) `quot` (pa - 1) {-# INLINE sigmaHelper #-} -totient :: (UniqueFactorisation n, Integral n) => n -> n+totient :: (UniqueFactorisation n, Num n) => n -> n totient = runFunction totientA  -- | Calculates the totient of a positive number @n@, i.e. --   the number of @k@ with @1 <= k <= n@ and @'gcd' n k == 1@, --   in other words, the order of the group of units in @&#8484;/(n)@.-totientA :: forall n. (UniqueFactorisation n, Integral n) => ArithmeticFunction n n+totientA :: forall n. (UniqueFactorisation n, Num n) => ArithmeticFunction n n totientA = multiplicative $ \((unPrime :: Prime n -> n) -> p) -> jordanHelper p -jordan :: (UniqueFactorisation n, Integral n) => Word -> n -> n+jordan :: (UniqueFactorisation n, Num n) => Word -> n -> n jordan = runFunction . jordanA  -- | Calculates the k-th Jordan function of an argument. -- -- > jordanA 1 = totientA-jordanA :: forall n. (UniqueFactorisation n, Integral n) => Word -> ArithmeticFunction n n+jordanA :: forall n. (UniqueFactorisation n, Num n) => Word -> ArithmeticFunction n n jordanA 0 = multiplicative $ \_ _ -> 0 jordanA 1 = totientA jordanA a = multiplicative $ \((unPrime :: Prime n -> n) -> p) -> jordanHelper (p ^ wordToInt a) -jordanHelper :: Integral n => n -> Word -> n+jordanHelper :: Num n => n -> Word -> n jordanHelper pa 1 = pa - 1 jordanHelper pa 2 = (pa - 1) * pa jordanHelper pa k = (pa - 1) * pa ^ wordToInt (k - 1) {-# INLINE jordanHelper #-} -moebius :: (UniqueFactorisation n, Num a) => n -> a+moebius :: UniqueFactorisation n => n -> Moebius moebius = runFunction moebiusA --- | Calculates the Moebius function of an argument.-moebiusA :: Num a => ArithmeticFunction n a-moebiusA = ArithmeticFunction (const f) runMoebius+-- | Calculates the Möbius function of an argument.+moebiusA :: ArithmeticFunction n Moebius+moebiusA = ArithmeticFunction (const f) id   where     f 1 = MoebiusN     f 0 = MoebiusP@@ -220,28 +219,6 @@ -- | The exponent of von Mangoldt function. Use @log expMangoldtA@ to recover von Mangoldt function itself. expMangoldtA :: forall n. (UniqueFactorisation n, Num n) => ArithmeticFunction n n expMangoldtA = ArithmeticFunction (\((unPrime :: Prime n -> n) -> p) _ -> MangoldtOne p) runMangoldt--data Moebius-  = MoebiusZ-  | MoebiusP-  | MoebiusN--runMoebius :: Num a => Moebius -> a-runMoebius m = case m of-  MoebiusZ ->  0-  MoebiusP ->  1-  MoebiusN -> -1--instance Semigroup Moebius where-  MoebiusZ <> _ = MoebiusZ-  _ <> MoebiusZ = MoebiusZ-  MoebiusP <> a = a-  a <> MoebiusP = a-  _ <> _ = MoebiusP--instance Monoid Moebius where-  mempty = MoebiusP-  mappend = (<>)  data Mangoldt a   = MangoldtZero
Math/NumberTheory/GCD.hs view
@@ -20,11 +20,17 @@ -- -- When using this module, always compile with optimisations turned on to -- benefit from GHC's primops and the rewrite rules.-{-# LANGUAGE CPP, BangPatterns, MagicHash #-}++{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP          #-}+{-# LANGUAGE LambdaCase   #-}+{-# LANGUAGE MagicHash    #-}+ module Math.NumberTheory.GCD     ( binaryGCD     , extendedGCD     , coprime+    , splitIntoCoprimes     ) where  import Data.Bits@@ -245,3 +251,37 @@  cw32 :: Word32 -> Word32 -> Bool cw32 (W32# x#) (W32# y#) = coprimeWord# x# y#++-- | The input list is assumed to be a factorisation of some number+-- into a list of powers of (possibly, composite) non-zero factors. The output+-- list is a factorisation of the same number such that all factors+-- are coprime. Such transformation is crucial to continue factorisation+-- (lazily, in parallel or concurrent fashion) without+-- having to merge multiplicities of primes, which occurs more than in one+-- composite factor.+--+-- > > splitIntoCoprimes [(140, 1), (165, 1)]+-- > [(5,2),(28,1),(33,1)]+-- > > splitIntoCoprimes [(360, 1), (210, 1)]+-- > [(2,4),(3,3),(5,2),(7,1)]+splitIntoCoprimes :: (Integral a, Eq b, Num b) => [(a, b)] -> [(a, b)]+splitIntoCoprimes xs = if any ((== 0) . fst) ys then [(0, 1)] else go ys+  where+    ys = filter (/= (0, 0)) xs++    go = \case+      [] -> []+      ((1,  _) : rest) -> go rest+      ((x, xm) : rest) -> case popSuchThat (\(r, _) -> gcd x r /= 1) rest of+        Nothing            -> (x, xm) : go rest+        Just ((y, ym), zs) -> let g = gcd x y in+          go ((g, xm + ym) : (x `quot` g, xm) : (y `quot` g, ym) : zs)++popSuchThat :: (a -> Bool) -> [a] -> Maybe (a, [a])+popSuchThat predicate = go+  where+    go = \case+      [] -> Nothing+      (x : xs)+        | predicate x -> Just (x, xs)+        | otherwise   -> fmap (fmap (x :)) (go xs)
Math/NumberTheory/GaussianIntegers.hs view
@@ -1,6 +1,6 @@ -- | -- Module:      Math.NumberTheory.GaussianIntegers--- Copyright:   (c) 2016 Chris Fredrickson+-- Copyright:   (c) 2016 Chris Fredrickson, Google Inc. -- Licence:     MIT -- Maintainer:  Chris Fredrickson <chris.p.fredrickson@gmail.com> -- Stability:   Provisional@@ -190,42 +190,38 @@  -- |Compute the prime factorisation of a Gaussian integer. This is unique up to units (+/- 1, +/- i). factorise :: GaussianInteger -> [(GaussianInteger, Int)]-factorise g-    | g == 0    = error "0 has no prime factorisation"-    | g == 1    = []-    | otherwise =-        let helper :: [(Integer, Int)] -> GaussianInteger -> [(GaussianInteger, Int)] -> [(GaussianInteger, Int)]-            helper [] g' fs = (if g' == 1 then [] else [(g', 1)]) ++ fs    -- include the unit, if it isn't 1-            helper ((!p, !e) : pt) g' fs-                | p `mod` 4 == 3 =-                    -- prime factors congruent to 3 mod 4 are simple.-                    let pow = div e 2-                        gp = fromInteger p-                    in helper pt (g' `divG` (gp .^ pow)) ((gp, pow) : fs)-                | otherwise      =-                    -- general case: for every prime factor of the magnitude-                    -- squared, find a Gaussian prime whose magnitude squared-                    -- is that prime. Then find out how many times the original-                    -- number is divisible by that Gaussian prime and its-                    -- conjugate. The order that the prime factors are-                    -- processed doesn't really matter, but it is reversed so-                    -- that the Gaussian primes will be in order of increasing-                    -- magnitude.-                    let gp = findPrime' p-                        (!gNext, !facs) = trialDivide g' [gp, abs $ conjugate gp] []-                    in helper pt gNext (facs ++ fs)-        in helper (reverse . Factorisation.factorise $ norm g) g []+factorise g = helper (Factorisation.factorise $ norm g) g+    where+    helper [] g' = if g' == 1 then [] else [(g', 1)] -- include the unit, if it isn't 1+    helper ((!p, !e) : pt) g' =+        -- For a given prime factor p of the magnitude squared...+        let (!g'', !facs) = if p `mod` 4 == 3+            then+                -- if the p is congruent to 3 (mod 4), then g' is divisible by+                -- p^(e/2).+                let pow = div e 2+                    gp = fromInteger p+                in (g' `divG` (gp .^ pow), [(gp, pow)])+            else+                -- otherwise: find a Gaussian prime gp for which `norm gp ==+                -- p`. Then do trial divisions to find out how many times g' is+                -- divisible by gp or its conjugate.+                let gp = findPrime' p+                in trialDivide g' [gp, abs $ conjugate gp]+        in facs ++ helper pt g''  -- Divide a Gaussian integer by a set of (relatively prime) Gaussian integers, -- as many times as possible, and return the final quotient as well as a count -- of how many times each factor divided the original.-trialDivide :: GaussianInteger -> [GaussianInteger] -> [(GaussianInteger, Int)] -> (GaussianInteger, [(GaussianInteger, Int)])-trialDivide g [] fs = (g, fs)-trialDivide g (pf : pft) fs-    | g `modG` pf == 0 =-        let (cnt, g') = countEvenDivisions g pf-        in trialDivide g' pft ((pf, cnt) : fs)-    | otherwise    = trialDivide g pft fs+trialDivide :: GaussianInteger -> [GaussianInteger] -> (GaussianInteger, [(GaussianInteger, Int)])+trialDivide = helper []+    where+    helper fs g [] = (g, fs)+    helper fs g (pf : pft)+        | g `modG` pf == 0 =+            let (cnt, g') = countEvenDivisions g pf+            in helper ((pf, cnt) : fs) g' pft+        | otherwise        = helper fs g pft  -- Divide a Gaussian integer by a possible factor, and return how many times -- the factor divided it evenly, as well as the result of dividing the original
Math/NumberTheory/Moduli/Class.hs view
@@ -9,14 +9,15 @@ -- Safe modular arithmetic with modulo on type level. -- -{-# LANGUAGE CPP                 #-}-{-# LANGUAGE DataKinds           #-}-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE KindSignatures      #-}-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE RankNTypes          #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving  #-}+{-# LANGUAGE CPP                        #-}+{-# LANGUAGE DataKinds                  #-}+{-# LANGUAGE GADTs                      #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures             #-}+{-# LANGUAGE LambdaCase                 #-}+{-# LANGUAGE RankNTypes                 #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE StandaloneDeriving         #-}  module Math.NumberTheory.Moduli.Class   ( -- * Known modulo@@ -40,12 +41,18 @@ import Data.Proxy import Data.Ratio import Data.Type.Equality+import GHC.Integer.GMP.Internals+import GHC.TypeNats.Compat+ #if __GLASGOW_HASKELL__ < 709 import Data.Word+import Numeric.Natural (Natural)++powModNatural :: Natural -> Natural -> Natural -> Natural+powModNatural b e m = fromInteger $ powModInteger (toInteger b) (toInteger e) (toInteger m)+#else+import GHC.Natural (Natural, powModNatural) #endif-import GHC.Integer.GMP.Internals-import GHC.TypeNats.Compat-import Numeric.Natural  -- | Wrapper for residues modulo @m@. --@@ -60,11 +67,15 @@ -- -- Note that modulo cannot be negative. newtype Mod (m :: Nat) = Mod Natural-  deriving (Eq, Ord)+  deriving (Eq, Ord, Enum)  instance KnownNat m => Show (Mod m) where   show m = "(" ++ show (getVal m) ++ " `modulo` " ++ show (getMod m) ++ ")" +instance KnownNat m => Bounded (Mod m) where+  minBound = Mod 0+  maxBound = let mx = Mod (getNatMod mx - 1) in mx+ instance KnownNat m => Num (Mod m) where   mx@(Mod x) + Mod y =     Mod $ if xy >= m then xy - m else xy@@ -148,7 +159,7 @@ powMod :: (KnownNat m, Integral a) => Mod m -> a -> Mod m powMod mx a   | a < 0     = error $ "^{Mod}: negative exponent"-  | otherwise = Mod $ fromInteger $ powModInteger (getVal mx) (toInteger a) (getMod mx)+  | otherwise = Mod $ powModNatural (getNatVal mx) (fromIntegral a) (getNatMod mx) {-# INLINABLE [1] powMod #-}  {-# SPECIALISE [1] powMod ::
Math/NumberTheory/Moduli/Jacobi.hs view
@@ -20,7 +20,9 @@  import Data.Array.Unboxed import Data.Bits+#if __GLASGOW_HASKELL__ < 803 import Data.Semigroup+#endif #if __GLASGOW_HASKELL__ < 709 import Data.Word #endif
+ Math/NumberTheory/Moduli/PrimitiveRoot.hs view
@@ -0,0 +1,148 @@+-- |+-- Module:      Math.NumberTheory.Moduli.PrimitiveRoot+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Primitive roots and cyclic groups.+--++{-# LANGUAGE CPP                  #-}+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE LambdaCase           #-}+{-# LANGUAGE StandaloneDeriving   #-}+{-# LANGUAGE TupleSections        #-}+{-# LANGUAGE UndecidableInstances #-}++module Math.NumberTheory.Moduli.PrimitiveRoot+  ( isPrimitiveRoot+    -- * Cyclic groups+  , CyclicGroup(..)+  , cyclicGroupFromModulo+  , cyclicGroupToModulo+  , isPrimitiveRoot'+  ) where++#if !(MIN_VERSION_base(4,8,0))+import Control.Applicative+import Data.Word+#endif++import Math.NumberTheory.ArithmeticFunctions (totient)+import Math.NumberTheory.Moduli (Mod, getNatMod, getNatVal, KnownNat)+import Math.NumberTheory.Powers.General (highestPower)+import Math.NumberTheory.Powers.Modular+import Math.NumberTheory.Prefactored+import Math.NumberTheory.UniqueFactorisation+import Math.NumberTheory.Utils.FromIntegral++-- | A multiplicative group of residues is called cyclic,+-- if there is a primitive root @g@,+-- whose powers generates all elements.+-- Any cyclic multiplicative group of residues+-- falls into one of the following cases.+data CyclicGroup a+  = CG2 -- ^ Residues modulo 2.+  | CG4 -- ^ Residues modulo 4.+  | CGOddPrimePower       (Prime a) Word+  -- ^ Residues modulo @p@^@k@ for __odd__ prime @p@.+  | CGDoubleOddPrimePower (Prime a) Word+  -- ^ Residues modulo 2@p@^@k@ for __odd__ prime @p@.++deriving instance Show (Prime a) => Show (CyclicGroup a)++-- | Check whether a multiplicative group of residues,+-- characterized by its modulo, is cyclic and, if yes, return its form.+--+-- > > cyclicGroupFromModulo 4+-- > Just CG4+-- > > cyclicGroupFromModulo (2 * 13 ^ 3)+-- > Just (CGDoubleOddPrimePower (PrimeNat 13) 3)+-- > > cyclicGroupFromModulo (4 * 13)+-- > Nothing+cyclicGroupFromModulo+  :: (Ord a, Integral a, UniqueFactorisation a)+  => a+  -> Maybe (CyclicGroup a)+cyclicGroupFromModulo = \case+  2 -> Just CG2+  4 -> Just CG4+  n+    | n <= 1    -> Nothing+    | odd n     -> uncurry CGOddPrimePower       <$> isPrimePower n+    | odd halfN -> uncurry CGDoubleOddPrimePower <$> isPrimePower halfN+    | otherwise -> Nothing+    where+      halfN = n `quot` 2++isPrimePower+  :: (Integral a, UniqueFactorisation a)+  => a+  -> Maybe (Prime a, Word)+isPrimePower n = (, intToWord k) <$> isPrime m+  where+    (m, k) = highestPower n++-- | Extract modulo and its factorisation from+-- a cyclic multiplicative group of residues.+--+-- > > cyclicGroupToModulo CG4+-- > Prefactored {prefValue = 4, prefFactors = [(2, 2)]}+-- > > cyclicGroupToModulo (CGDoubleOddPrimePower (PrimeNat 13) 3)+-- > Prefactored {prefValue = 4394, prefFactors = [(2, 1), (13, 3)]}+cyclicGroupToModulo+  :: (Integral a, UniqueFactorisation a)+  => CyclicGroup a+  -> Prefactored a+cyclicGroupToModulo = fromFactors . \case+  CG2                       -> [(2, 1)]+  CG4                       -> [(2, 2)]+  CGOddPrimePower p k       -> [(unPrime p, k)]+  CGDoubleOddPrimePower p k -> [(2, 1), (unPrime p, k)]++-- | 'isPrimitiveRoot'' @cg@ @a@ checks whether @a@ is+-- a <https://en.wikipedia.org/wiki/Primitive_root_modulo_n primitive root>+-- of a given cyclic multiplicative group of residues @cg@.+--+-- > > let Just cg = cyclicGroupFromModulo 13+-- > > isPrimitiveRoot cg 1+-- > False+-- > > isPrimitiveRoot cg 2+-- > True+isPrimitiveRoot'+  :: (Integral a, UniqueFactorisation a)+  => CyclicGroup a+  -> a+  -> Bool+isPrimitiveRoot' cg r = r /= 0 && gcd r (prefValue m) == 1 && all (/= 1) exps+  where+    -- https://en.wikipedia.org/wiki/Primitive_root_modulo_n#Finding_primitive_roots+    m    = cyclicGroupToModulo cg+    phi  = totient m+    pows = map (\pk -> prefValue phi `quot` unPrime (fst pk)) (factorise phi)+    exps = map (\p -> powMod r p (prefValue m)) pows++-- | Check whether a given modular residue is+-- a <https://en.wikipedia.org/wiki/Primitive_root_modulo_n primitive root>.+--+-- > > isPrimitiveRoot (1 :: Mod 13)+-- > False+-- > > isPrimitiveRoot (2 :: Mod 13)+-- > True+--+-- Here is how to list all primitive roots:+--+-- > > filter isPrimitiveRoot [minBound .. maxBound] :: [Mod 13]+-- > [(2 `modulo` 13), (6 `modulo` 13), (7 `modulo` 13), (11 `modulo` 13)]+--+-- This function is a convenient wrapper around 'isPrimitiveRoot''. The latter+-- provides better control and performance, if you need them.+isPrimitiveRoot+  :: KnownNat n+  => Mod n+  -> Bool+isPrimitiveRoot r = case cyclicGroupFromModulo (getNatMod r) of+  Nothing -> False+  Just cg -> isPrimitiveRoot' cg (getNatVal r)
Math/NumberTheory/MoebiusInversion.hs view
@@ -6,7 +6,7 @@ -- Stability:   Provisional -- Portability: Non-portable (GHC extensions) ----- Generalised Moebius inversion+-- Generalised Möbius inversion -- {-# LANGUAGE BangPatterns, FlexibleContexts #-} module Math.NumberTheory.MoebiusInversion@@ -22,7 +22,7 @@ import Math.NumberTheory.Unsafe  -- | @totientSum n@ is, for @n > 0@, the sum of @[totient k | k <- [1 .. n]]@,---   computed via generalised Moebius inversion.+--   computed via generalised Möbius inversion. --   See <http://mathworld.wolfram.com/TotientSummatoryFunction.html> for the --   formula used for @totientSum@. totientSum :: Int -> Integer@@ -32,10 +32,10 @@   where     triangle k = (k*(k+1)) `quot` 2 --- | @generalInversion g n@ evaluates the generalised Moebius inversion of @g@+-- | @generalInversion g n@ evaluates the generalised Möbius inversion of @g@ --   at the argument @n@. -----   The generalised Moebius inversion implemented here allows an efficient+--   The generalised Möbius inversion implemented here allows an efficient --   calculation of isolated values of the function @f : N -> Z@ if the function --   @g@ defined by --@@ -43,15 +43,15 @@ -- > g n = sum [f (n `quot` k) | k <- [1 .. n]] -- > -----   can be cheaply computed. By the generalised Moebius inversion formula, then+--   can be cheaply computed. By the generalised Möbius inversion formula, then -- -- > -- > f n = sum [moebius k * g (n `quot` k) | k <- [1 .. n]] -- > -- --   which allows the computation in /O/(n) steps, if the values of the---   Moebius function are known. A slightly different formula, used here,---   does not need the values of the Moebius function and allows the+--   Möbius function are known. A slightly different formula, used here,+--   does not need the values of the Möbius function and allows the --   computation in /O/(n^0.75) steps, using /O/(n^0.5) memory. -- --   An example of a pair of such functions where the inversion allows a@@ -78,7 +78,7 @@ --   method is only appropriate to compute isolated values of @f@. generalInversion :: (Int -> Integer) -> Int -> Integer generalInversion fun n-    | n < 1     = error "Moebius inversion only defined on positive domain"+    | n < 1     = error "Möbius inversion only defined on positive domain"     | n == 1    = fun 1     | n == 2    = fun 2 - fun 1     | n == 3    = fun 3 - 2*fun 1
Math/NumberTheory/MoebiusInversion/Int.hs view
@@ -6,7 +6,7 @@ -- Stability:   Provisional -- Portability: Non-portable (GHC extensions) ----- Generalised Moebius inversion for 'Int' valued functions.+-- Generalised Möbius inversion for 'Int' valued functions. -- {-# LANGUAGE BangPatterns, FlexibleContexts #-} {-# OPTIONS_GHC -fspec-constr-count=8 #-}@@ -23,7 +23,7 @@ import Math.NumberTheory.Unsafe  -- | @totientSum n@ is, for @n > 0@, the sum of @[totient k | k <- [1 .. n]]@,---   computed via generalised Moebius inversion.+--   computed via generalised Möbius inversion. --   See <http://mathworld.wolfram.com/TotientSummatoryFunction.html> for the --   formula used for @totientSum@. totientSum :: Int -> Int@@ -33,10 +33,10 @@   where     triangle k = (k*(k+1)) `quot` 2 --- | @generalInversion g n@ evaluates the generalised Moebius inversion of @g@+-- | @generalInversion g n@ evaluates the generalised Möbius inversion of @g@ --   at the argument @n@. -----   The generalised Moebius inversion implemented here allows an efficient+--   The generalised Möbius inversion implemented here allows an efficient --   calculation of isolated values of the function @f : N -> Z@ if the function --   @g@ defined by --@@ -44,15 +44,15 @@ -- > g n = sum [f (n `quot` k) | k <- [1 .. n]] -- > -----   can be cheaply computed. By the generalised Moebius inversion formula, then+--   can be cheaply computed. By the generalised Möbius inversion formula, then -- -- > -- > f n = sum [moebius k * g (n `quot` k) | k <- [1 .. n]] -- > -- --   which allows the computation in /O/(n) steps, if the values of the---   Moebius function are known. A slightly different formula, used here,---   does not need the values of the Moebius function and allows the+--   Möbius function are known. A slightly different formula, used here,+--   does not need the values of the Möbius function and allows the --   computation in /O/(n^0.75) steps, using /O/(n^0.5) memory. -- --   An example of a pair of such functions where the inversion allows a@@ -79,7 +79,7 @@ --   method is only appropriate to compute isolated values of @f@. generalInversion :: (Int -> Int) -> Int -> Int generalInversion fun n-    | n < 1     = error "Moebius inversion only defined on positive domain"+    | n < 1     = error "Möbius inversion only defined on positive domain"     | n == 1    = fun 1     | n == 2    = fun 2 - fun 1     | n == 3    = fun 3 - 2*fun 1
Math/NumberTheory/Powers.hs view
@@ -32,9 +32,13 @@   , exactRoot   , isPerfectPower   , highestPower+    -- * Modular powers+  , powMod   ) where  import Math.NumberTheory.Powers.Squares import Math.NumberTheory.Powers.Cubes import Math.NumberTheory.Powers.Fourth import Math.NumberTheory.Powers.General++import Math.NumberTheory.Powers.Modular
− Math/NumberTheory/Powers/Integer.hs
@@ -1,44 +0,0 @@--- |--- Module:      Math.NumberTheory.Powers.Integer--- Copyright:   (c) 2011-2014 Daniel Fischer--- Licence:     MIT--- Maintainer:  Daniel Fischer <daniel.is.fischer@googlemail.com>--- Stability:   Provisional--- Portability: Non-portable (GHC extensions)------ Potentially faster power function for 'Integer' base and 'Int'--- or 'Word' exponent.----{-# LANGUAGE CPP #-}-module Math.NumberTheory.Powers.Integer-    {-# DEPRECATED "It is no faster than (^)" #-}-    ( integerPower-    , integerWordPower-    ) where--#if MIN_VERSION_base(4,8,0)-#else-import Data.Word-#endif---- | Power of an 'Integer' by the left-to-right repeated squaring algorithm.---   This needs two multiplications in each step while the right-to-left---   algorithm needs only one multiplication for 0-bits, but here the---   two factors always have approximately the same size, which on average---   gains a bit when the result is large.------   For small results, it is unlikely to be any faster than '(^)', quite---   possibly slower (though the difference shouldn't be large), and for---   exponents with few bits set, the same holds. But for exponents with---   many bits set, the speedup can be significant.------   /Warning:/ No check for the negativity of the exponent is performed,---   a negative exponent is interpreted as a large positive exponent.-integerPower :: Integer -> Int -> Integer-integerPower = (^)-{-# DEPRECATED integerPower "Use (^) instead" #-}---- | Same as 'integerPower', but for exponents of type 'Word'.-integerWordPower :: Integer -> Word -> Integer-integerWordPower = (^)-{-# DEPRECATED integerWordPower "Use (^) instead" #-}
+ Math/NumberTheory/Powers/Modular.hs view
@@ -0,0 +1,97 @@+-- |+-- Module:      Math.NumberTheory.Powers.Modular+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Modular powers (a. k. a. modular exponentiation).+--++{-# LANGUAGE CPP       #-}+{-# LANGUAGE MagicHash #-}++module Math.NumberTheory.Powers.Modular+  ( powMod+#if __GLASGOW_HASKELL__ > 709+  , powModWord+  , powModInt+#endif+  ) where++import qualified GHC.Integer.GMP.Internals as GMP (powModInteger)++#if __GLASGOW_HASKELL__ > 709+import GHC.Exts (Word(..))+import GHC.Natural (powModNatural)+import qualified GHC.Integer.GMP.Internals as GMP (powModWord)+import Math.NumberTheory.Utils.FromIntegral+#endif++-- | @powMod@ @b@ @e@ @m@ computes (@b^e@) \`mod\` @m@ in effective way.+-- An exponent @e@ must be non-negative, a modulo @m@ must be positive.+-- Otherwise the behaviour of @powMod@ is undefined.+--+-- > > powMod 2 3 5+-- > 3+-- > > powMod 3 12345678901234567890 1001+-- > 1+--+-- See also 'Math.NumberTheory.Moduli.Class.powMod' and 'Math.NumberTheory.Moduli.Class.powSomeMod'.+--+-- For finite numeric types ('Int', 'Word', etc.)+-- modulo @m@ should be such that @m^2@ does not overflow,+-- otherwise the behaviour is undefined. If you+-- need both to fit into machine word and to handle large moduli,+-- take a look at 'powModInt' and 'powModWord'.+--+-- > > powMod 3 101 (2^60-1 :: Integer)+-- > 1018105167100379328 -- correct+-- > > powMod 3 101 (2^60-1 :: Int64)+-- > 1115647832265427613 -- incorrect due to overflow+-- > > powModInt 3 101 (2^60-1 :: Int)+-- > 1018105167100379328 -- correct+powMod :: (Integral a, Integral b) => a -> b -> a -> a+powMod x y m+  | m <= 0    = error "powModInt: non-positive modulo"+  | y <  0    = error "powModInt: negative exponent"+  | otherwise = f (x `rem` m) y 1 `mod` m+  where+    f _ 0 acc = acc+    f b e acc = f (b * b `rem` m) (e `quot` 2)+      (if odd e then (b * acc `rem` m) else acc)++{-# INLINE [1] powMod #-}+{-# RULES+"powMod/Integer" powMod = powModInteger+  #-}++-- Work around https://ghc.haskell.org/trac/ghc/ticket/14085+powModInteger :: Integer -> Integer -> Integer -> Integer+powModInteger b e m = GMP.powModInteger (b `mod` m) e m++#if __GLASGOW_HASKELL__ > 709+{-# RULES+"powMod/Natural" powMod = powModNatural+"powMod/Word"    powMod = powModWord+"powMod/Int"     powMod = powModInt+  #-}++-- | Specialised version of 'powMod', able to handle large moduli correctly.+--+-- > > powModWord 3 101 (2^60-1)+-- > 1018105167100379328+powModWord :: Word -> Word -> Word -> Word+powModWord (W# x) (W# y) (W# m) = W# (GMP.powModWord x y m)++-- | Specialised version of 'powMod', able to handle large moduli correctly.+--+-- > > powModInt 3 101 (2^60-1)+-- > 1018105167100379328+powModInt :: Int -> Int -> Int -> Int+powModInt x y m+  | m <= 0 = error "powModInt: non-positive modulo"+  | y <  0 = error "powModInt: negative exponent"+  | otherwise = wordToInt $ powModWord (intToWord (x `mod` m)) (intToWord y) (intToWord m)+#endif
+ Math/NumberTheory/Prefactored.hs view
@@ -0,0 +1,130 @@+-- |+-- Module:      Math.NumberTheory.Prefactored+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Type for numbers, accompanied by their factorisation.+--++{-# LANGUAGE CPP          #-}+{-# LANGUAGE TypeFamilies #-}++module Math.NumberTheory.Prefactored+  ( Prefactored(prefValue, prefFactors)+  , fromValue+  , fromFactors+  ) where++import Control.Arrow++import Math.NumberTheory.GCD (splitIntoCoprimes)+import Math.NumberTheory.UniqueFactorisation++#if MIN_VERSION_base(4,8,0)+#else+import Data.Word+#endif++-- | A container for a number and its pairwise coprime (but not neccessarily prime)+-- factorisation.+-- It is designed to preserve information about factors under multiplication.+-- One can use this representation to speed up prime factorisation+-- and computation of arithmetic functions.+--+-- For instance, let @p@ and @q@ be big primes:+--+-- > > let p, q :: Integer+-- > >     p = 1000000000000000000000000000057+-- > >     q = 2000000000000000000000000000071+--+-- It would be  difficult to compute prime factorisation of their product+-- as is:+-- 'factorise' would take ages. Things become different if we simply+-- change types of @p@ and @q@ to prefactored ones:+--+-- > > let p, q :: Prefactored Integer+-- > >     p = 1000000000000000000000000000057+-- > >     q = 2000000000000000000000000000071+--+-- Now prime factorisation is done instantly:+--+-- > > factorise (p * q)+-- > [(PrimeNat 1000000000000000000000000000057, 1), (PrimeNat 2000000000000000000000000000071, 1)]+-- > > factorise (p^2 * q^3)+-- > [(PrimeNat 1000000000000000000000000000057, 2), (PrimeNat 2000000000000000000000000000071, 3)]+--+-- Moreover, we can instantly compute 'totient' and its iterations.+-- It works fine, because output of 'totient' is also prefactored.+--+-- > > prefValue $ totient (p^2 * q^3)+-- > 8000000000000000000000000001752000000000000000000000000151322000000000000000000000006445392000000000000000000000135513014000000000000000000001126361040+-- > > prefValue $ totient $ totient (p^2 * q^3)+-- > 2133305798262843681544648472180210822742702690942899511234131900112583590230336435053688694839034890779375223070157301188739881477320529552945446912000+--+-- Let us look under the hood:+--+-- > > prefFactors $ totient (p^2 * q^3)+-- > [(2, 4), (41666666666666666666666666669, 1), (3, 3), (111111111111111111111111111115, 1),+-- > (1000000000000000000000000000057, 1), (2000000000000000000000000000071, 2)]+-- > > prefFactors $ totient $ totient (p^2 * q^3)+-- > [(2, 22), (39521, 1), (5, 3), (199937, 1), (3, 8), (6046667, 1), (227098769, 1),+-- > (85331809838489, 1), (361696272343, 1), (22222222222222222222222222223, 1),+-- > (41666666666666666666666666669, 1), (2000000000000000000000000000071, 1)]+--+-- Pairwise coprimality of factors is crucial, because it allows+-- us to process them independently, possibly even+-- in parallel or concurrent fashion.+--+-- Following invariant is guaranteed to hold:+--+-- > abs (prefValue x) = abs (product (map (uncurry (^)) (prefFactors x)))+data Prefactored a = Prefactored+  { prefValue   :: a+    -- ^ Number itself.+  , prefFactors :: [(a, Word)]+    -- ^ List of pairwise coprime (but not neccesarily prime) factors,+    -- accompanied by their multiplicities.+  } deriving (Show)++-- | Create 'Prefactored' from a given number.+--+-- > > fromValue 123+-- > Prefactored {prefValue = 123, prefFactors = [(123, 1)]}+fromValue :: a -> Prefactored a+fromValue a = Prefactored a [(a, 1)]++-- | Create 'Prefactored' from a given list of pairwise coprime+-- (but not neccesarily prime) factors with multiplicities.+-- If you cannot ensure coprimality, use 'splitIntoCoprimes'.+--+-- > > fromFactors (splitIntoCoprimes [(140, 1), (165, 1)])+-- > Prefactored {prefValue = 23100, prefFactors = [(5, 2), (28, 1), (33, 1)]}+-- > > fromFactors (splitIntoCoprimes [(140, 2), (165, 3)])+-- > Prefactored {prefValue = 88045650000, prefFactors = [(5, 5), (28, 2), (33, 3)]}+fromFactors :: Integral a => [(a, Word)] -> Prefactored a+fromFactors as = Prefactored (product (map (uncurry (^)) as)) (splitIntoCoprimes as)++instance (Integral a, UniqueFactorisation a) => Num (Prefactored a) where+  Prefactored v1 _ + Prefactored v2 _+    = fromValue (v1 + v2)+  Prefactored v1 _ - Prefactored v2 _+    = fromValue (v1 - v2)+  Prefactored v1 f1 * Prefactored v2 f2+    = Prefactored (v1 * v2) (splitIntoCoprimes (f1 ++ f2))+  negate (Prefactored v f) = Prefactored (negate v) f+  abs (Prefactored v f)    = Prefactored (abs v) f+  signum (Prefactored v _) = Prefactored (signum v) []+  fromInteger n = fromValue (fromInteger n)++type instance Prime (Prefactored a) = Prime a++instance UniqueFactorisation a => UniqueFactorisation (Prefactored a) where+  unPrime p = fromValue (unPrime p)+  factorise (Prefactored _ f)+    = concatMap (\(x, xm) -> map (second (* xm)) (factorise x)) f+  isPrime (Prefactored _ f) = case f of+    [(n, 1)] -> isPrime n+    _        -> Nothing
Math/NumberTheory/Primes/Factorisation/Montgomery.hs view
@@ -21,11 +21,12 @@ -- Given enough time, the algorithm should be able to factor numbers of 100-120 digits, but it -- is best suited for numbers of up to 50-60 digits. -{-# LANGUAGE BangPatterns   #-}-{-# LANGUAGE CPP            #-}-{-# LANGUAGE DataKinds      #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE LambdaCase     #-}+{-# LANGUAGE BangPatterns        #-}+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE KindSignatures      #-}+{-# LANGUAGE LambdaCase          #-}+{-# LANGUAGE ScopedTypeVariables #-}  {-# OPTIONS_GHC -fno-warn-type-defaults #-} {-# OPTIONS_HADDOCK hide #-}@@ -48,10 +49,9 @@   , findParms   ) where -#include "MachDeps.h"-+import Control.Arrow import System.Random-import Control.Monad.State.Strict+import Control.Monad.State.Lazy #if __GLASGOW_HASKELL__ < 709 import Control.Applicative import Data.Word@@ -61,10 +61,14 @@ import qualified Data.IntMap as IM import Data.List (foldl') import Data.Maybe+#if __GLASGOW_HASKELL__ < 803+import Data.Semigroup+#endif  import GHC.TypeNats.Compat  import Math.NumberTheory.Curves.Montgomery+import Math.NumberTheory.GCD (splitIntoCoprimes) import Math.NumberTheory.Moduli.Class import Math.NumberTheory.Powers.General     (highestPower, largePFPower) import Math.NumberTheory.Powers.Squares     (integerSquareRoot')@@ -138,7 +142,7 @@ stdGenFactorisation primeBound sg digits n     = curveFactorisation primeBound bailliePSW (\m -> randomR (6,m-2)) sg digits n --- | @'curveFactorisation'@ is the driver for the factorisation. Its performance (and success)+-- | 'curveFactorisation' is the driver for the factorisation. Its performance (and success) --   can be influenced by passing appropriate arguments. If you know that @n@ has no prime divisors --   below @b@, any divisor found less than @b*b@ must be prime, thus giving @Just (b*b)@ as the --   first argument allows skipping the comparatively expensive primality test for those.@@ -150,78 +154,105 @@ --   make a huge difference. So, if the default takes too long, try another one; or you can improve your --   chances for a quick result by running several instances in parallel. -----   @'curveFactorisation'@ requires that small prime factors have been stripped before. Also, it is---   unlikely to succeed if @n@ has more than one (really) large prime factor.-curveFactorisation :: Maybe Integer                 -- ^ Lower bound for composite divisors-                   -> (Integer -> Bool)             -- ^ A primality test-                   -> (Integer -> g -> (Integer,g)) -- ^ A PRNG-                   -> g                             -- ^ Initial PRNG state-                   -> Maybe Int                     -- ^ Estimated number of digits of the smallest prime factor-                   -> Integer                       -- ^ The number to factorise-                   -> [(Integer,Int)]               -- ^ List of prime factors and exponents+--   'curveFactorisation' @n@ requires that small (< 100000) prime factors of @n@+--   have been stripped before. Otherwise it is likely to cycle forever. When in doubt,+--   use 'defaultStdGenFactorisation'.+--+--   'curveFactorisation' is unlikely to succeed if @n@ has more than one (really) large prime factor.+--+curveFactorisation+  :: forall g.+     Maybe Integer                  -- ^ Lower bound for composite divisors+  -> (Integer -> Bool)              -- ^ A primality test+  -> (Integer -> g -> (Integer, g)) -- ^ A PRNG+  -> g                              -- ^ Initial PRNG state+  -> Maybe Int                      -- ^ Estimated number of digits of the smallest prime factor+  -> Integer                        -- ^ The number to factorise+  -> [(Integer, Int)]               -- ^ List of prime factors and exponents curveFactorisation primeBound primeTest prng seed mbdigs n-    | ptest n   = [(n,1)]+    | n == 1    = []+    | ptest n   = [(n, 1)]     | otherwise = evalState (fact n digits) seed       where+        digits :: Int         digits = fromMaybe 8 mbdigs-        mult 1 xs = xs-        mult j xs = [(p,j*k) | (p,k) <- xs]-        dbl (u,v) = (mult 2 u, mult 2 v)-        ptest = case primeBound of-                  Just bd -> \k -> k <= bd || primeTest k-                  Nothing -> primeTest-        rndR k = state (\gen -> prng k gen)-        perfPw = case primeBound of-                   Nothing -> highestPower-                   Just bd -> largePFPower (integerSquareRoot' bd)-        fact m digs = do let (b1,b2,ct) = findParms digs-                         (pfs,cfs) <- repFact m b1 b2 ct-                         if null cfs-                           then return pfs-                           else do-                               nfs <- forM cfs $ \(k,j) ->-                                   mult j <$> fact k (if null pfs then digs+5 else digs)-                               return (mergeAll $ pfs:nfs)-        repFact m b1 b2 count = case perfPw m of-                                  (_,1) -> workFact m b1 b2 count-                                  (b,e)-                                    | ptest b -> return ([(b,e)],[])-                                    | otherwise -> do-                                      (as,bs) <- workFact b b1 b2 count-                                      return $ (mult e as, mult e bs)-        workFact m b1 b2 count-            | count == 0 = return ([],[(m,1)])-            | otherwise = do-                s <- rndR m-                case s `modulo` fromInteger m of-                  InfMod{} -> error "impossible case"-                  SomeMod sm -> case montgomeryFactorisation b1 b2 sm of-                    Nothing -> workFact m b1 b2 (count-1)-                    Just d  -> do-                      let !cof = m `quot` d-                      case gcd cof d of-                        1 -> do-                            (dp,dc) <- if ptest d-                                         then return ([(d,1)],[])-                                         else repFact d b1 b2 (count-1)-                            (cp,cc) <- if ptest cof-                                         then return ([(cof,1)],[])-                                         else repFact cof b1 b2 (count-1)-                            return (merge dp cp, dc ++ cc)-                        g -> do-                            let d' = d `quot` g-                                c' = cof `quot` g-                            (dp,dc) <- if ptest d'-                                         then return ([(d',1)],[])-                                         else repFact d' b1 b2 (count-1)-                            (cp,cc) <- if ptest c'-                                         then return ([(c',1)],[])-                                         else repFact c' b1 b2 (count-1)-                            (gp,gc) <- if ptest g-                                         then return ([(g,2)],[])-                                         else dbl <$> repFact g b1 b2 (count-1)-                            return  (mergeAll [dp,cp,gp], dc ++ cc ++ gc) +        ptest :: Integer -> Bool+        ptest = maybe primeTest (\bd k -> k <= bd || primeTest k) primeBound++        rndR :: Integer -> State g Integer+        rndR k = state (prng k)++        perfPw :: Integer -> (Integer, Int)+        perfPw = maybe highestPower (largePFPower . integerSquareRoot') primeBound++        fact :: Integer -> Int -> State g [(Integer, Int)]+        fact 1 _ = return mempty+        fact m digs = do+          let (b1, b2, ct) = findParms digs+          -- All factors (both @pfs@ and @cfs@), are pairwise coprime. This is+          -- because 'repFact' returns either a single factor, or output of 'workFact'.+          -- In its turn, 'workFact' returns either a single factor,+          -- or concats 'repFact's over coprime integers. Induction completes the proof.+          Factors pfs cfs <- repFact m b1 b2 ct+          case cfs of+            [] -> return pfs+            _  -> do+              nfs <- forM cfs $ \(k, j) ->+                  map (second (* j)) <$> fact k (if null pfs then digs + 5 else digs)+              return $ mconcat (pfs : nfs)++        repFact :: Integer -> Word -> Word -> Word -> State g Factors+        repFact 1 _ _ _ = return mempty+        repFact m b1 b2 count =+          case perfPw m of+            (_, 1) -> workFact m b1 b2 count+            (b, e)+              | ptest b   -> return $ singlePrimeFactor b e+              | otherwise -> modifyPowers (* e) <$> workFact b b1 b2 count++        workFact :: Integer -> Word -> Word -> Word -> State g Factors+        workFact 1 _ _ _ = return mempty+        workFact m _ _ 0 = return $ singleCompositeFactor m 1+        workFact m b1 b2 count = do+          s <- rndR m+          case s `modulo` fromInteger m of+            InfMod{} -> error "impossible case"+            SomeMod sm -> case montgomeryFactorisation b1 b2 sm of+              Nothing -> workFact m b1 b2 (count - 1)+              Just d  -> do+                let cs = splitIntoCoprimes [(d, 1), (m `quot` d, 1)]+                -- Since all @cs@ are coprime, we can factor each of+                -- them and just concat results, without summing up+                -- powers of the same primes in different elements.+                fmap mconcat $ flip mapM cs $+                  \(x, xm) -> if ptest x+                              then pure $ singlePrimeFactor x xm+                              else repFact x b1 b2 (count - 1)++data Factors = Factors+  { _primeFactors     :: [(Integer, Int)]+  , _compositeFactors :: [(Integer, Int)]+  }++singlePrimeFactor :: Integer -> Int -> Factors+singlePrimeFactor a b = Factors [(a, b)] []++singleCompositeFactor :: Integer -> Int -> Factors+singleCompositeFactor a b = Factors [] [(a, b)]++instance Semigroup Factors where+  Factors pfs1 cfs1 <> Factors pfs2 cfs2+    = Factors (pfs1 <> pfs2) (cfs1 <> cfs2)++instance Monoid Factors where+  mempty = Factors [] []+  mappend = (<>)++modifyPowers :: (Int -> Int) -> Factors -> Factors+modifyPowers f (Factors pfs cfs)+  = Factors (map (second f) pfs) (map (second f) cfs)+ ---------------------------------------------------------------------------------------------------- --                                         The workhorse                                          -- ----------------------------------------------------------------------------------------------------@@ -331,22 +362,6 @@                         (k,r) | r == 1 -> ([(p,k)], Nothing)                               | otherwise -> (p,k) <: go r ps     go m [] = ([(m,1)], Nothing)---- helpers: merge sorted lists-merge :: (Ord a, Num b) => [(a, b)] -> [(a, b)] -> [(a, b)]-merge xs [] = xs-merge [] ys = ys-merge xxs@(x@(p, k) : xs) yys@(y@(q, m) : ys)-  = case p `compare` q of-    LT -> x          : merge xs yys-    EQ -> (p, k + m) : merge xs  ys-    GT -> y          : merge xxs ys--mergeAll :: (Ord a, Num b) => [[(a, b)]] -> [(a, b)]-mergeAll = \case-  []              -> []-  [xs]            -> xs-  (xs : ys : zss) -> merge (merge xs ys) (mergeAll zss)  -- | For a given estimated decimal length of the smallest prime factor -- ("tier") return parameters B1, B2 and the number of curves to try
Math/NumberTheory/Primes/Heap.hs view
@@ -17,7 +17,7 @@ {-# LANGUAGE BangPatterns, CPP, MonoLocalBinds #-} {-# OPTIONS_GHC -funbox-strict-fields #-} {-# OPTIONS_GHC -fno-float-in -fno-spec-constr -fno-full-laziness #-}-module Math.NumberTheory.Primes.Heap (primes, sieveFrom) where+module Math.NumberTheory.Primes.Heap {-# DEPRECATED "Use Math.NumberTheory.Primes.Sieve instead" #-} (primes, sieveFrom) where  import Data.Array.Unboxed import Data.Array.ST
Math/NumberTheory/Primes/Sieve/Misc.hs view
@@ -46,6 +46,8 @@ import Math.NumberTheory.Unsafe import Math.NumberTheory.Utils +{-# DEPRECATED FactorSieve, TotientSieve, CarmichaelSieve, factorSieve, sieveFactor, fsBound, fsPrimeTest, totientSieve, sieveTotient, carmichaelSieve, sieveCarmichael "Use new interface for sieves, provided by \"Math.NumberTheory.ArithmeticFunctions.SieveBlock\"" #-}+ {- IMPORTANT NOTICE: Not all sieves use the same layout! 
Math/NumberTheory/Primes/Testing.hs view
@@ -7,6 +7,9 @@ -- Portability: Non-portable (GHC extensions) -- -- Primality tests.++{-# OPTIONS_GHC -fno-warn-deprecations #-}+ module Math.NumberTheory.Primes.Testing     ( -- * Standard tests       isPrime@@ -27,6 +30,8 @@ import Math.NumberTheory.Primes.Testing.Certified import Math.NumberTheory.Primes.Factorisation.TrialDivision import Math.NumberTheory.Primes.Sieve.Misc++{-# DEPRECATED fsIsPrime "Use new interface for sieves, provided by Math.NumberTheory.ArithmeticFunctions.SieveBlock" #-}  -- | Test primality using a 'FactorSieve'. If @n@ is out of bounds --   of the sieve, fall back to 'isPrime'.
+ Math/NumberTheory/Primes/Types.hs view
@@ -0,0 +1,53 @@+-- |+-- Module:      Math.NumberTheory.Primes.Types+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- This is an internal module, defining types for primes.+-- Should not be exposed to users.+--++{-# LANGUAGE CPP          #-}+{-# LANGUAGE TypeFamilies #-}++module Math.NumberTheory.Primes.Types+  ( Prime+  , Prm(..)+  , PrimeNat(..)+  ) where++#if MIN_VERSION_base(4,8,0)+#else+import Data.Word+#endif++import Numeric.Natural++newtype Prm = Prm { unPrm :: Word }+  deriving (Eq, Ord)++instance Show Prm where+  showsPrec d (Prm p) r = (if d > 10 then "(" ++ s ++ ")" else s) ++ r+    where+      s = "Prm " ++ show p++newtype PrimeNat = PrimeNat { unPrimeNat :: Natural }+  deriving (Eq, Ord)++instance Show PrimeNat where+  showsPrec d (PrimeNat p) r = (if d > 10 then "(" ++ s ++ ")" else s) ++ r+    where+      s = "PrimeNat " ++ show p++-- | Type of primes of a given unique factorisation domain.+--+-- @abs (unPrime n) == unPrime n@ must hold for all @n@ of type @Prime t@+type family Prime (f :: *) :: *++type instance Prime Int     = Prm+type instance Prime Word    = Prm+type instance Prime Integer = PrimeNat+type instance Prime Natural = PrimeNat
+ Math/NumberTheory/SmoothNumbers.hs view
@@ -0,0 +1,130 @@+-- |+-- Module:      Math.NumberTheory.SmoothNumbers+-- Copyright:   (c) 2018 Frederick Schneider+-- Licence:     MIT+-- Maintainer:  Frederick Schneider <frederick.schneider2011@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- A <https://en.wikipedia.org/wiki/Smooth_number smooth number>+-- is an integer, which can be represented as a product of powers of elements+-- from a given set (smooth basis). E. g., 48 = 3 * 4 * 4 is smooth+-- over a set {3, 4}, and 24 is not.+--++module Math.NumberTheory.SmoothNumbers+  ( -- * Create a smooth basis+    SmoothBasis+  , fromSet+  , fromList+  , fromSmoothUpperBound+    -- * Generate smooth numbers+  , smoothOver+  , smoothOverInRange+  , smoothOverInRangeBF+  ) where++import Data.List (sort, nub)+import qualified Data.Set as S+import Math.NumberTheory.Primes.Sieve (primes)++-- | An abstract representation of a smooth basis.+-- It consists of a set of coprime numbers ≥2.+newtype SmoothBasis a = SmoothBasis { unSmoothBasis :: [a] } deriving Show++-- | Build a 'SmoothBasis' from a set of coprime numbers ≥2.+--+-- > > fromSet (Set.fromList [2, 3])+-- > Just (SmoothBasis [2, 3])+-- > > fromSet (Set.fromList [2, 4]) -- should be coprime+-- > Nothing+-- > > fromSet (Set.fromList [1, 3]) -- should be >= 2+-- > Nothing+fromSet :: Integral a => S.Set a -> Maybe (SmoothBasis a)+fromSet s = if isValid l then Just (SmoothBasis l) else Nothing where l = S.elems s++-- | Build a 'SmoothBasis' from a list of coprime numbers ≥2.+--+-- > > fromList [2, 3]+-- > Just (SmoothBasis [2, 3])+-- > > fromList [2, 2]+-- > Just (SmoothBasis [2])+-- > > fromList [2, 4] -- should be coprime+-- > Nothing+-- > > fromList [1, 3] -- should be >= 2+-- > Nothing+fromList :: Integral a => [a] -> Maybe (SmoothBasis a)+fromList l = if isValid l' then Just (SmoothBasis l') else Nothing where l' = nub $ sort l++-- | Build a 'SmoothBasis' from a list of primes below given bound.+--+-- > > fromSmoothUpperBound 10+-- > Just (SmoothBasis [2, 3, 5, 7])+-- > > fromSmoothUpperBound 1+-- > Nothing+fromSmoothUpperBound :: Integral a => a -> Maybe (SmoothBasis a)+fromSmoothUpperBound n = if (n < 2)+                         then Nothing+                         else Just $ SmoothBasis $ map fromInteger $ takeWhile (<= nI) primes+                         where nI = toInteger n++-- | Generate an infinite ascending list of+-- <https://en.wikipedia.org/wiki/Smooth_number smooth numbers>+-- over a given smooth basis.+--+-- > > take 10 (smoothOver (fromJust (fromList [2, 5])))+-- > [1, 2, 4, 5, 8, 10, 16, 20, 25, 32]+smoothOver :: Integral a => SmoothBasis a -> [a]+smoothOver pl = foldr (\p l -> mergeListLists $ iterate (map (p*)) l) [1] (unSmoothBasis pl)+                where+                      {-# INLINE mergeListLists #-}+                      mergeListLists      = foldr go1 []+                        where go1 (h:t) b = h:(go2 t b)+                              go1 _     b = b++                              go2 a@(ah:at) b@(bh:bt)+                                | bh < ah   = bh : (go2 a bt)+                                | otherwise = ah : (go2 at b) -- no possibility of duplicates+                              go2 a b = if null a then b else a++-- | Generate an ascending list of+-- <https://en.wikipedia.org/wiki/Smooth_number smooth numbers>+-- over a given smooth basis in a given range.+--+-- It may appear inefficient+-- for short, but distant ranges;+-- consider using 'smoothOverInRangeBF' in such cases.+--+-- > > smoothOverInRange (fromJust (fromList [2, 5])) 100 200+-- > [100, 125, 128, 160, 200]+smoothOverInRange   :: Integral a => SmoothBasis a -> a -> a -> [a]+smoothOverInRange s lo hi = takeWhile (<= hi) $ dropWhile (< lo) (smoothOver s)++-- | Generate an ascending list of+-- <https://en.wikipedia.org/wiki/Smooth_number smooth numbers>+-- over a given smooth basis in a given range.+--+-- It is inefficient+-- for large or starting near 0 ranges;+-- consider using 'smoothOverInRange' in such cases.+--+-- Suffix BF stands for the brute force algorithm, involving a lot of divisions.+--+-- > > smoothOverInRangeBF (fromJust (fromList [2, 5])) 100 200+-- > [100, 125, 128, 160, 200]+smoothOverInRangeBF :: Integral a => SmoothBasis a -> a -> a -> [a]+smoothOverInRangeBF prs lo hi = filter (mf prs') [lo..hi]+                                where mf []     n    = (n == 1) -- mf means manually factor+                                      mf pl@(p:ps) n = if (mod n p == 0)+                                                       then mf pl (div n p)+                                                       else mf ps n+                                      prs'           = unSmoothBasis prs++-- | isValid assumes that the list is sorted and unique and then checks if the list is suitable to be a SmoothBasis.+isValid :: (Integral a) => [a] -> Bool+isValid pl = if (length pl == 0) then False else v' pl+             where v' []        = True+                   v' (x:xs)    = if (x < 2 || (not $ rpl x xs)) then False else v' xs+                   rpl _ []     = True  -- rpl means relatively prime to the rest of the list+                   rpl n (x:xs) = if (gcd n x > 1) then False else rpl n xs+
Math/NumberTheory/UniqueFactorisation.hs view
@@ -26,29 +26,13 @@ #endif  import Math.NumberTheory.Primes.Factorisation as F (factorise')-import Math.NumberTheory.GaussianIntegers as G+import Math.NumberTheory.Primes.Testing.Probabilistic as T (isPrime)+import Math.NumberTheory.Primes.Types (Prime, Prm(..), PrimeNat(..))+import qualified Math.NumberTheory.GaussianIntegers as G+import Math.NumberTheory.Utils.FromIntegral  import Numeric.Natural -newtype SmallPrime = SmallPrime { _unSmallPrime :: Word }-  deriving (Eq, Ord, Show)--newtype BigPrime = BigPrime { _unBigPrime :: Natural }-  deriving (Eq, Ord, Show)---- | Type of primes of a given unique factorisation domain.--- When the domain has exactly one unit, @Prime t = t@,--- but when units are multiple more restricted types--- (or at least newtypes) should be specified.------ @abs (unPrime n) == unPrime n@ must hold for all @n@ of type @Prime t@-type family Prime (f :: *) :: *--type instance Prime Int     = SmallPrime-type instance Prime Word    = SmallPrime-type instance Prime Integer = BigPrime-type instance Prime Natural = BigPrime- type instance Prime G.GaussianInteger = GaussianPrime  -- | The following invariant must hold for @n /= 0@:@@ -60,6 +44,13 @@   unPrime   :: Prime a -> a   factorise :: a -> [(Prime a, Word)] +  isPrime   :: a -> Maybe (Prime a)+  isPrime n = case factorise n of+    [(p, 1)] -> Just p+    _        -> Nothing++  {-# MINIMAL unPrime, factorise #-}+ instance UniqueFactorisation Int where   unPrime   = coerce wordToInt   factorise m' = if m <= 1@@ -67,13 +58,14 @@                 else map (coerce integerToWord *** intToWord) . F.factorise' . intToInteger $ m                   where                     m = abs m'+  isPrime n = if T.isPrime (toInteger n) then Just (coerce $ intToWord $ abs n) else Nothing  instance UniqueFactorisation Word where   unPrime     = coerce   factorise m = if m <= 1                   then []                   else map (coerce integerToWord *** intToWord) . F.factorise' . wordToInteger $ m-+  isPrime n = if T.isPrime (toInteger n) then Just (coerce n) else Nothing  instance UniqueFactorisation Integer where   unPrime      = coerce naturalToInteger@@ -82,12 +74,14 @@                 else map (coerce integerToNatural *** intToWord) . F.factorise' $ m                   where                     m = abs m'+  isPrime n = if T.isPrime n then Just (coerce $ integerToNatural $ abs n) else Nothing  instance UniqueFactorisation Natural where   unPrime   = coerce   factorise m = if m <= 1                   then []                   else map (coerce integerToNatural *** intToWord) . F.factorise' . naturalToInteger $ m+  isPrime n = if T.isPrime (toInteger n) then Just (coerce n) else Nothing  newtype GaussianPrime = GaussianPrime { _unGaussianPrime :: G.GaussianInteger }   deriving (Eq, Show)@@ -97,27 +91,3 @@    factorise 0 = []   factorise g = map (coerce *** intToWord) $ filter (\(h, _) -> abs h /= 1) $ G.factorise g---------------- Utils--wordToInt :: Word -> Int-wordToInt = fromIntegral--wordToInteger :: Word -> Integer-wordToInteger = fromIntegral--intToWord :: Int -> Word-intToWord = fromIntegral--intToInteger :: Int -> Integer-intToInteger = fromIntegral--naturalToInteger :: Natural -> Integer-naturalToInteger = fromIntegral--integerToNatural :: Integer -> Natural-integerToNatural = fromIntegral--integerToWord :: Integer -> Word-integerToWord = fromIntegral
Math/NumberTheory/Utils.hs view
@@ -20,6 +20,7 @@     , bitCountWord#     , uncheckedShiftR     , splitOff+    , splitOff#     ) where  #include "MachDeps.h"@@ -191,14 +192,22 @@                 case (v2 `plusWord#` uncheckedShiftRL# v2 4#) `and#` mf## of                   v3 -> word2Int# (uncheckedShiftRL# (v3 `timesWord#` m1##) sd#) --- {-# SPECIALISE splitOff :: Integer -> Integer -> (Int, Integer),---                            Int -> Int -> (Int, Int),---                            Word -> Word -> (Int, Word)---   #-}-{-# INLINABLE splitOff #-}-splitOff :: Integral a => a -> a -> (Int, a)+splitOff :: Integer -> Integer -> (Int, Integer)+splitOff _ 0 = (0, 0) -- prevent infinite loop splitOff p n = go 0 n   where     go !k m = case m `quotRem` p of-                (q,r) | r == 0 -> go (k+1) q-                      | otherwise -> (k,m)+      (q, 0) -> go (k + 1) q+      _      -> (k, m)+{-# INLINABLE splitOff #-}++-- | It is difficult to convince GHC to unbox output of 'splitOff' and 'splitOff.go',+-- so we fallback to a specialized unboxed version to minimize allocations.+splitOff# :: Word# -> Word# -> (# Int#, Word# #)+splitOff# _ 0## = (# 0#, 0## #)+splitOff# p n = go 0# n+  where+    go k m = case m `quotRemWord#` p of+      (# q, 0## #) -> go (k +# 1#) q+      _            -> (# k, m #)+{-# INLINABLE splitOff# #-}
+ Math/NumberTheory/Utils/FromIntegral.hs view
@@ -0,0 +1,55 @@+-- |+-- Module:      Math.NumberTheory.Utils.FromIntegral+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Monomorphic `fromIntegral`.+--++{-# LANGUAGE CPP                 #-}++module Math.NumberTheory.Utils.FromIntegral+  ( wordToInt+  , wordToInteger+  , intToWord+  , intToInteger+  , naturalToInteger+  , integerToNatural+  , integerToWord+  ) where++import Numeric.Natural+#if __GLASGOW_HASKELL__ < 709+import Data.Word+#endif++wordToInt :: Word -> Int+wordToInt = fromIntegral+{-# INLINE wordToInt #-}++wordToInteger :: Word -> Integer+wordToInteger = fromIntegral+{-# INLINE wordToInteger #-}++intToWord :: Int -> Word+intToWord = fromIntegral+{-# INLINE intToWord #-}++intToInteger :: Int -> Integer+intToInteger = fromIntegral+{-# INLINE intToInteger #-}++naturalToInteger :: Natural -> Integer+naturalToInteger = fromIntegral+{-# INLINE naturalToInteger #-}++integerToNatural :: Integer -> Natural+integerToNatural = fromIntegral+{-# INLINE integerToNatural #-}++integerToWord :: Integer -> Word+integerToWord = fromIntegral+{-# INLINE integerToWord #-}
+ Math/NumberTheory/Utils/Hyperbola.hs view
@@ -0,0 +1,92 @@+-- |+-- Module:      Math.NumberTheory.Utils.Hyperbola+-- Copyright:   (c) 2018 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+-- Portability: Non-portable (GHC extensions)+--+-- Highest points under hyperbola.+--++{-# LANGUAGE CPP        #-}+{-# LANGUAGE LambdaCase #-}++module Math.NumberTheory.Utils.Hyperbola+  ( pointsUnderHyperbola+  ) where++import Data.Bits++import Math.NumberTheory.Powers.Cubes++-- | Straightforward computation of+-- [ n `quot` x | x <- [hi, hi - 1 .. lo] ].+-- Unfortunately, such list generator performs poor,+-- so we fall back to manual recursion.+pointsUnderHyperbola0 :: Int -> Int -> Int -> [Int]+pointsUnderHyperbola0 n lo hi+  | n < 0     = error "pointsUnderHyperbola0: first argument must be non-negative"+  | lo <= 0   = error "pointsUnderHyperbola0: second argument must be positive"+  | otherwise = go hi+    where+      go x+        | x < lo    = []+        | otherwise = n `quot` x : go (x - 1)++data Bresenham = Bresenham+  {  bresX       :: !Int+  ,  bresBeta    :: !Int+  , _bresGamma   :: !Int+  , _bresDelta1  :: !Int+  , _bresEpsilon :: !Int+  }++initBresenham :: Int -> Int -> Bresenham+initBresenham n x = Bresenham x beta gamma delta1 epsilon+  where+    beta    = n `quot` x+    epsilon = n `rem` x+    delta1  = n `quot` (x - 1) - beta+    gamma   = beta - (x - 1) * delta1++-- | bresenham(x+1) -> bresenham(x) for x >= (2n)^1/3+stepBack :: Bresenham -> Bresenham+stepBack (Bresenham x' beta' gamma' delta1' epsilon') =+  if eps >= x+    then (if eps >= x `shiftL` 1+      then {- delta2 = 2 -}+        let delta1 = delta1' + 2 in (Bresenham x (beta' + delta1) (gamma' + delta1 `shiftL` 1 - x `shiftL` 1) delta1 (eps - x `shiftL` 1))+      else {- delta1 = 1 -}+        let delta1 = delta1' + 1 in (Bresenham x (beta' + delta1) (gamma' + delta1 `shiftL` 1 - x) delta1 (eps - x))+      )+    else (if eps >= 0+      then {- delta2 =  0 -}+        (Bresenham x (beta' + delta1') (gamma' + delta1' `shiftL` 1) delta1' eps)+      else {- delta2 = -1 -}+        let delta1 = delta1' - 1 in (Bresenham x (beta' + delta1) (gamma' + delta1 `shiftL` 1 + x) delta1 (eps + x)))+  where+    x       = x' - 1+    eps     = epsilon' + gamma'+{-# INLINE stepBack #-}++-- | Division-free computation of+-- [ n `quot` x | x <- [hi, hi - 1 .. lo] ].+-- In other words, we compute y-coordinates of highest integral points+-- under hyperbola @x * y = n@ between @x = lo@ and @x = hi@ in reverse order.+--+-- The implementation follows section 5 of <https://arxiv.org/pdf/1206.3369.pdf A successive approximation algorithm for computing the divisor summatory function>+-- by R. Sladkey.+-- It is 2x faster than a trivial implementation for 'Int'.+pointsUnderHyperbola :: Int -> Int -> Int -> [Int]+pointsUnderHyperbola n lo hi+  | n < 0        = error "pointsUnderHyperbola: first argument must be non-negative"+  | lo <= 0      = error "pointsUnderHyperbola: second argument must be positive"+  | hi <  lo     = []+  | hi == lo     = [n `quot` lo]+  | otherwise    = go (initBresenham n hi)+  where+    mid = (integerCubeRoot (2 * n) + 1) `max` lo+    go h+      | bresX h < mid = pointsUnderHyperbola0 n lo ((mid - 1) `min` hi)+      | otherwise = bresBeta h : go (stepBack h)
arithmoi.cabal view
@@ -1,8 +1,8 @@ name                : arithmoi-version             : 0.6.0.1+version             : 0.7.0.0 cabal-version       : >= 1.10 author              : Daniel Fischer-copyright           : (c) 2011 Daniel Fischer, 2016-2017 Andrew Lelechenko, Carter Schonwald+copyright           : (c) 2011 Daniel Fischer, 2016-2018 Andrew Lelechenko, Carter Schonwald license             : MIT license-file        : LICENSE maintainer          : Carter Schonwald  carter at wellposed dot com,@@ -42,6 +42,7 @@                         , mtl >= 2.0 && < 2.3                         , exact-pi >= 0.4.1.1                         , integer-logarithms >= 1.0+                        , vector >= 0.12     if impl(ghc < 7.10)       build-depends     : nats >= 1 && <1.2     if impl(ghc < 8.0)@@ -49,12 +50,16 @@      exposed-modules     : Math.NumberTheory.ArithmeticFunctions                           Math.NumberTheory.ArithmeticFunctions.Class+                          Math.NumberTheory.ArithmeticFunctions.Mertens+                          Math.NumberTheory.ArithmeticFunctions.Moebius+                          Math.NumberTheory.ArithmeticFunctions.SieveBlock                           Math.NumberTheory.ArithmeticFunctions.Standard                           Math.NumberTheory.Curves.Montgomery                           Math.NumberTheory.Moduli                           Math.NumberTheory.Moduli.Chinese                           Math.NumberTheory.Moduli.Class                           Math.NumberTheory.Moduli.Jacobi+                          Math.NumberTheory.Moduli.PrimitiveRoot                           Math.NumberTheory.Moduli.Sqrt                           Math.NumberTheory.MoebiusInversion                           Math.NumberTheory.MoebiusInversion.Int@@ -69,7 +74,7 @@                           Math.NumberTheory.Powers.Cubes                           Math.NumberTheory.Powers.Fourth                           Math.NumberTheory.Powers.General-                          Math.NumberTheory.Powers.Integer+                          Math.NumberTheory.Powers.Modular                           Math.NumberTheory.Primes                           Math.NumberTheory.Primes.Sieve                           Math.NumberTheory.Primes.Factorisation@@ -80,8 +85,14 @@                           Math.NumberTheory.Primes.Heap                           Math.NumberTheory.UniqueFactorisation                           Math.NumberTheory.Zeta+                          Math.NumberTheory.Prefactored                           GHC.TypeNats.Compat-    other-modules       : Math.NumberTheory.Utils+                          Math.NumberTheory.SmoothNumbers++    other-modules       : Math.NumberTheory.ArithmeticFunctions.SieveBlock.Unboxed+                          Math.NumberTheory.Utils+                          Math.NumberTheory.Utils.FromIntegral+                          Math.NumberTheory.Utils.Hyperbola                           Math.NumberTheory.Unsafe                           Math.NumberTheory.Primes.Counting.Impl                           Math.NumberTheory.Primes.Counting.Approximate@@ -93,7 +104,8 @@                           Math.NumberTheory.Primes.Testing.Probabilistic                           Math.NumberTheory.Primes.Testing.Certified                           Math.NumberTheory.Primes.Testing.Certificates.Internal-    other-extensions          : BangPatterns+                          Math.NumberTheory.Primes.Types+    other-extensions    : BangPatterns      ghc-options         : -O2 -Wall     ghc-prof-options    : -O2 -auto@@ -108,16 +120,22 @@ benchmark criterion   build-depends:    base                     , arithmoi-                    , criterion+                    , gauge                     , containers                     , random                     , integer-logarithms+                    , vector   if impl(ghc < 7.10)     build-depends     : nats >= 1 && <1.2+  if impl(ghc < 8.0)+    build-depends     : semigroups >= 0.8   other-modules:    Math.NumberTheory.ArithmeticFunctionsBench+                  , Math.NumberTheory.GCDBench+                  , Math.NumberTheory.MertensBench                   , Math.NumberTheory.PowersBench                   , Math.NumberTheory.PrimesBench                   , Math.NumberTheory.RecurrenciesBench+                  , Math.NumberTheory.SieveBlockBench   hs-source-dirs:   benchmark   main-is:          Bench.hs   type:             exitcode-stdio-1.0@@ -132,13 +150,14 @@   build-depends:        arithmoi                       , base >= 4.6 && < 5                       , containers >= 0.5 && < 0.6-                      , tasty >= 0.10 && < 0.12+                      , tasty >= 0.10 && < 1.1                       , tasty-smallcheck >= 0.8 && < 0.9-                      , tasty-quickcheck >= 0.9 && < 0.10-                      , tasty-hunit >= 0.9 && < 0.10-                      , QuickCheck >= 2.10 && < 2.11+                      , tasty-quickcheck >= 0.9 && < 0.11+                      , tasty-hunit >= 0.9 && < 0.11+                      , QuickCheck >= 2.10 && < 2.12                       , transformers >= 0.5                       , integer-gmp < 1.1+                      , vector   if impl(ghc < 7.10)     build-depends     : smallcheck >= 1.1 && < 1.1.3,                         nats >= 1 && <1.2@@ -148,6 +167,8 @@     build-depends     : semigroups >= 0.8    other-modules :   Math.NumberTheory.ArithmeticFunctionsTests+                  , Math.NumberTheory.ArithmeticFunctions.MertensTests+                  , Math.NumberTheory.ArithmeticFunctions.SieveBlockTests                   , Math.NumberTheory.CurvesTests                   , Math.NumberTheory.GaussianIntegersTests                   , Math.NumberTheory.GCDTests@@ -157,17 +178,19 @@                   , Math.NumberTheory.Moduli.ChineseTests                   , Math.NumberTheory.Moduli.ClassTests                   , Math.NumberTheory.Moduli.JacobiTests+                  , Math.NumberTheory.Moduli.PrimitiveRootTests                   , Math.NumberTheory.Moduli.SqrtTests                   , Math.NumberTheory.Powers.CubesTests                   , Math.NumberTheory.MoebiusInversionTests                   , Math.NumberTheory.MoebiusInversion.IntTests                   , Math.NumberTheory.Powers.FourthTests                   , Math.NumberTheory.Powers.GeneralTests+                  , Math.NumberTheory.Powers.ModularTests                   , Math.NumberTheory.Powers.SquaresTests+                  , Math.NumberTheory.PrefactoredTests                   , Math.NumberTheory.PrimesTests                   , Math.NumberTheory.Primes.CountingTests                   , Math.NumberTheory.Primes.FactorisationTests-                  , Math.NumberTheory.Primes.HeapTests                   , Math.NumberTheory.Primes.SieveTests                   , Math.NumberTheory.Primes.TestingTests                   , Math.NumberTheory.TestUtils
benchmark/Bench.hs view
@@ -1,15 +1,21 @@ module Main where -import Criterion.Main+import Gauge.Main  import Math.NumberTheory.ArithmeticFunctionsBench as ArithmeticFunctions+import Math.NumberTheory.GCDBench as GCD+import Math.NumberTheory.MertensBench as Mertens import Math.NumberTheory.PowersBench as Powers import Math.NumberTheory.PrimesBench as Primes import Math.NumberTheory.RecurrenciesBench as Recurrencies+import Math.NumberTheory.SieveBlockBench as SieveBlock  main = defaultMain   [ ArithmeticFunctions.benchSuite+  , GCD.benchSuite+  , Mertens.benchSuite   , Powers.benchSuite   , Primes.benchSuite   , Recurrencies.benchSuite+  , SieveBlock.benchSuite   ]
benchmark/Math/NumberTheory/ArithmeticFunctionsBench.hs view
@@ -2,7 +2,7 @@   ( benchSuite   ) where -import Criterion.Main+import Gauge.Main import Data.Set (Set)  import Math.NumberTheory.ArithmeticFunctions as A@@ -13,12 +13,13 @@ compareSetFunctions :: String -> (Integer -> Set Integer) -> Benchmark compareSetFunctions name new = bench name $ nf (map new) [1..100000] +benchSuite :: Benchmark benchSuite = bgroup "ArithmeticFunctions"   [ compareSetFunctions "divisors" A.divisors   , bench "divisors/int" $ nf (map A.divisorsSmall) [1 :: Int .. 100000]   , compareFunctions "totient" A.totient   , compareFunctions "carmichael" A.carmichael-  , compareFunctions "moebius" A.moebius+  , compareFunctions "moebius" (A.runMoebius . A.moebius)   , compareFunctions "tau" A.tau   , compareFunctions "sigma 1" (A.sigma 1)   , compareFunctions "sigma 2" (A.sigma 2)
+ benchmark/Math/NumberTheory/GCDBench.hs view
@@ -0,0 +1,21 @@+module Math.NumberTheory.GCDBench+  ( benchSuite+  ) where++import Gauge.Main++import Math.NumberTheory.GCD as A+import Prelude as P++benchSuite = bgroup "GCD"+  [ subSuite "large coprimes" 1073741823 100003+  , subSuite "powers of 2" (2^12) (2^19)+  , subSuite "power of 23" (23^3) (23^7)+  ]+  where subSuite :: String -> Int -> Int -> Benchmark+        subSuite name m n = bgroup name+          [ bench "Prelude.gcd" $ nf (P.gcd m) n+          , bench "binaryGCD" $ nf (A.binaryGCD m) n+          , bench "Prelude.coprime" $ nf (\n -> 1 == P.gcd m n) n+          , bench "coprime" $ nf (A.coprime m) n+          ]
+ benchmark/Math/NumberTheory/MertensBench.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE CPP        #-}+{-# LANGUAGE LambdaCase #-}++{-# OPTIONS_GHC -fno-warn-type-defaults #-}++module Math.NumberTheory.MertensBench+  ( benchSuite+  ) where++import Gauge.Main+#if __GLASGOW_HASKELL__ < 709+import Data.Word+#endif++import Math.NumberTheory.ArithmeticFunctions.Mertens++mertensBench :: Word -> Benchmark+mertensBench n = bench (show n) (nf mertens n)++benchSuite :: Benchmark+benchSuite = bgroup "Mertens" $ map mertensBench $ take 4 $ iterate (* 10) 10000000
benchmark/Math/NumberTheory/PowersBench.hs view
@@ -1,8 +1,10 @@+{-# OPTIONS_GHC -fno-warn-type-defaults #-}+ module Math.NumberTheory.PowersBench   ( benchSuite   ) where -import Criterion.Main+import Gauge.Main import System.Random  import Math.NumberTheory.Logarithms (integerLog2)@@ -25,4 +27,5 @@   where     n = genInteger 0 bits +benchSuite :: Benchmark benchSuite = bgroup "Powers" $ map compareRoots [2300, 2400 .. 2600]
benchmark/Math/NumberTheory/PrimesBench.hs view
@@ -1,10 +1,10 @@-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}+{-# OPTIONS_GHC -fno-warn-type-defaults #-}  module Math.NumberTheory.PrimesBench   ( benchSuite   ) where -import Criterion.Main+import Gauge.Main import System.Random  import Math.NumberTheory.Logarithms (integerLog2)
benchmark/Math/NumberTheory/RecurrenciesBench.hs view
@@ -4,9 +4,7 @@   ( benchSuite   ) where -import Criterion.Main-import Numeric.Natural-import System.Random+import Gauge.Main  import Math.NumberTheory.Recurrencies.Bilinear @@ -21,6 +19,7 @@     benchAt i j = bench ("!! " ++ show i ++ " !! " ++ show j)                 $ nf (\(x, y) -> triangle !! x !! y :: Integer) (i, j) +benchSuite :: Benchmark benchSuite = bgroup "Bilinear"   [ benchTriangle "binomial"  binomial 1000   , benchTriangle "stirling1" stirling1 100
+ benchmark/Math/NumberTheory/SieveBlockBench.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE CPP        #-}+{-# LANGUAGE LambdaCase #-}++{-# OPTIONS_GHC -fno-warn-deprecations  #-}+{-# OPTIONS_GHC -fno-warn-type-defaults #-}++module Math.NumberTheory.SieveBlockBench+  ( benchSuite+  ) where++import Gauge.Main+#if __GLASGOW_HASKELL__ < 803+import Data.Semigroup+#endif+import qualified Data.Vector as V+import qualified Data.Vector.Unboxed as U+#if __GLASGOW_HASKELL__ < 709+import Data.Word+#endif++import Math.NumberTheory.ArithmeticFunctions.Moebius+import Math.NumberTheory.ArithmeticFunctions.SieveBlock+import Math.NumberTheory.Primes.Factorisation (totientSieve, sieveTotient, carmichaelSieve, sieveCarmichael)++blockLen :: Word+blockLen = 10^6++totientHelper :: Word -> Word -> Word+totientHelper p 1 = p - 1+totientHelper p 2 = (p - 1) * p+totientHelper p k = (p - 1) * p ^ (k - 1)++totientBlockConfig :: SieveBlockConfig Word+totientBlockConfig = SieveBlockConfig+  { sbcEmpty                = 1+  , sbcAppend               = (*)+  , sbcFunctionOnPrimePower = totientHelper+  }++sumOldTotientSieve :: Word -> Word+sumOldTotientSieve len' = sum $ map (fromInteger . sieveTotient sieve) [1 .. len]+  where+    len = toInteger len'+    sieve = totientSieve len++carmichaelHelper :: Word -> Word -> Word+carmichaelHelper 2 1 = 1+carmichaelHelper 2 2 = 2+carmichaelHelper 2 k = 2 ^ (k - 2)+carmichaelHelper p 1 = p - 1+carmichaelHelper p 2 = (p - 1) * p+carmichaelHelper p k = (p - 1) * p ^ (k - 1)++carmichaelBlockConfig :: SieveBlockConfig Word+carmichaelBlockConfig = SieveBlockConfig+  { sbcEmpty                = 1+  -- There is a specialized 'gcd' for Word, but not 'lcm'.+  , sbcAppend               = (\x y -> (x `quot` (gcd x y)) * y)+  , sbcFunctionOnPrimePower = carmichaelHelper+  }++sumOldCarmichaelSieve :: Word -> Word+sumOldCarmichaelSieve len' = sum $ map (fromInteger . sieveCarmichael sieve) [1 .. len]+  where+    len = toInteger len'+    sieve = carmichaelSieve len++moebiusConfig :: SieveBlockConfig Moebius+moebiusConfig = SieveBlockConfig+  { sbcEmpty                = MoebiusP+  , sbcAppend               = (<>)+  , sbcFunctionOnPrimePower = const $ \case+      0 -> MoebiusP+      1 -> MoebiusN+      _ -> MoebiusZ+  }++benchSuite :: Benchmark+benchSuite = bgroup "SieveBlock"+  [ bgroup "totient"+    [ bench "old"     $ nf sumOldTotientSieve blockLen+    , bench "boxed"   $ nf (V.sum . sieveBlock        totientBlockConfig 1) blockLen+    , bench "unboxed" $ nf (U.sum . sieveBlockUnboxed totientBlockConfig 1) blockLen+    ]+  , bgroup "carmichael"+    [ bench "old"     $ nf sumOldCarmichaelSieve blockLen+    , bench "boxed"   $ nf (V.sum . sieveBlock        carmichaelBlockConfig 1) blockLen+    , bench "unboxed" $ nf (U.sum . sieveBlockUnboxed carmichaelBlockConfig 1) blockLen+    ]+  , bgroup "moebius"+    [ bench "boxed"   $ nf (V.sum . V.map runMoebius . sieveBlock        moebiusConfig 1 :: Word -> Int) blockLen+    , bench "unboxed" $ nf (U.sum . U.map runMoebius . sieveBlockUnboxed moebiusConfig 1 :: Word -> Int) blockLen+    , bench "special" $ nf (U.sum . U.map runMoebius . sieveBlockMoebius 1 :: Word -> Int) blockLen+    ]+  ]
+ test-suite/Math/NumberTheory/ArithmeticFunctions/MertensTests.hs view
@@ -0,0 +1,76 @@+-- |+-- Module:      Math.NumberTheory.ArithmeticFunctions.MertensTests+-- Copyright:   (c) 2018 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+--+-- Tests for Math.NumberTheory.ArithmeticFunctions.Mertens+--++{-# LANGUAGE CPP        #-}+{-# LANGUAGE LambdaCase #-}++{-# OPTIONS_GHC -fno-warn-type-defaults #-}++module Math.NumberTheory.ArithmeticFunctions.MertensTests+  ( testSuite+  ) where++import Test.Tasty++#if __GLASGOW_HASKELL__ < 803+import Data.Semigroup+#endif+import qualified Data.Vector as V+import qualified Data.Vector.Unboxed as U+#if __GLASGOW_HASKELL__ < 709+import Data.Word+#endif++import Math.NumberTheory.ArithmeticFunctions+import Math.NumberTheory.ArithmeticFunctions.Mertens+import Math.NumberTheory.ArithmeticFunctions.SieveBlock+import Math.NumberTheory.TestUtils++moebiusConfig :: SieveBlockConfig Moebius+moebiusConfig = SieveBlockConfig+  { sbcEmpty                = MoebiusP+  , sbcAppend               = (<>)+  , sbcFunctionOnPrimePower = const $ \case+      0 -> MoebiusP+      1 -> MoebiusN+      _ -> MoebiusZ+  }++mertensDiffPointwise :: Word -> Word -> Int+mertensDiffPointwise lo len = sum $ map (runMoebius . moebius) [lo + 1 .. lo + len]++mertensDiffBlockSpecial :: Word -> Word -> Int+mertensDiffBlockSpecial lo len = U.sum $ U.map runMoebius+  $ sieveBlockMoebius (lo + 1) len++mertensDiffBlockUnboxed :: Word -> Word -> Int+mertensDiffBlockUnboxed lo len = U.sum $ U.map runMoebius+  $ sieveBlockUnboxed moebiusConfig (lo + 1) len++mertensDiffBlockBoxed :: Word -> Word -> Int+mertensDiffBlockBoxed lo len = V.sum $ V.map runMoebius+  $ sieveBlock moebiusConfig (lo + 1) len++mertensDiff :: Word -> Word -> Int+mertensDiff lo len = mertens (lo + len) - mertens lo++propertyCompare :: (Word -> Word -> Int) -> Word -> Word -> Bool+propertyCompare func lo' len' = mertensDiff lo len == func lo len+  where+    lo  = lo'  `rem` 10000000+    len = len' `rem` 1000++testSuite :: TestTree+testSuite = testGroup "Mertens"+  [ testSmallAndQuick "pointwise"     $ propertyCompare mertensDiffPointwise+  , testSmallAndQuick "block special" $ propertyCompare mertensDiffBlockSpecial+  , testSmallAndQuick "block unboxed" $ propertyCompare mertensDiffBlockUnboxed+  , testSmallAndQuick "block boxed"   $ propertyCompare mertensDiffBlockBoxed+  ]
+ test-suite/Math/NumberTheory/ArithmeticFunctions/SieveBlockTests.hs view
@@ -0,0 +1,109 @@+-- |+-- Module:      Math.NumberTheory.ArithmeticFunctions.SieveBlockTests+-- Copyright:   (c) 2016 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+--+-- Tests for Math.NumberTheory.ArithmeticFunctions.SieveBlock+--++{-# LANGUAGE CPP        #-}+{-# LANGUAGE LambdaCase #-}++{-# OPTIONS_GHC -fno-warn-type-defaults #-}++module Math.NumberTheory.ArithmeticFunctions.SieveBlockTests+  ( testSuite+  ) where++import Test.Tasty+import Test.Tasty.HUnit++#if __GLASGOW_HASKELL__ < 803+import Data.Semigroup+#endif+import qualified Data.Vector as V+import qualified Data.Vector.Unboxed as U+#if __GLASGOW_HASKELL__ < 709+import Data.Word+#endif++import Math.NumberTheory.ArithmeticFunctions+import Math.NumberTheory.ArithmeticFunctions.SieveBlock++pointwiseTest :: (Eq a, Show a) => ArithmeticFunction Word a -> Word -> Word -> IO ()+pointwiseTest f lowIndex len = assertEqual "pointwise"+    (runFunctionOverBlock f lowIndex len)+    (V.generate (fromIntegral len) (runFunction f . (+ lowIndex) . fromIntegral))++unboxedTest :: (Eq a, U.Unbox a, Show a) => SieveBlockConfig a -> IO ()+unboxedTest config = assertEqual "unboxed"+    (sieveBlock config 1 1000)+    (U.convert $ sieveBlockUnboxed config 1 1000)++moebiusTest :: Word -> Word -> Bool+moebiusTest m n+  = m == 0+  || sieveBlockUnboxed moebiusConfig m n+  == sieveBlockMoebius m n++moebiusSpecialCases :: [TestTree]+moebiusSpecialCases = map (uncurry pairToTest)+  [ (1, 1)+  , (1, 2)+  , (208, 298)+  , (1, 12835)+  , (10956, 4430)+  , (65, 16171)+  , (120906, 19456)+  , (33800000, 27002)+  , (17266222643, 5051)+  , (1000158, 48758)+  , (1307265, 3725)+  , (2600000, 14686)+  , (4516141422507 - 100000, 100001)+  , (1133551497049257 - 100000, 100001)+  -- too long for regular runs+  -- , (1157562178759482171 - 100000, 100001)+  ]+  where+    pairToTest :: Word -> Word -> TestTree+    pairToTest m n = testCase (show m ++ "," ++ show n) $ assertBool "should be equal" $ moebiusTest m n++multiplicativeConfig :: (Word -> Word -> Word) -> SieveBlockConfig Word+multiplicativeConfig f = SieveBlockConfig+  { sbcEmpty                = 1+  , sbcAppend               = (*)+  , sbcFunctionOnPrimePower = f+  }++moebiusConfig :: SieveBlockConfig Moebius+moebiusConfig = SieveBlockConfig+  { sbcEmpty = MoebiusP+  , sbcAppend = (<>)+  , sbcFunctionOnPrimePower = const $ \case+      0 -> MoebiusP+      1 -> MoebiusN+      _ -> MoebiusZ+  }++testSuite :: TestTree+testSuite = testGroup "SieveBlock"+  [ testGroup "pointwise"+    [ testCase "divisors"   $ pointwiseTest divisorsA   1 1000+    , testCase "tau"        $ pointwiseTest tauA        1 1000+    , testCase "totient"    $ pointwiseTest totientA    1 1000+    , testCase "moebius"    $ pointwiseTest moebiusA    1 1000+    , testCase "smallOmega" $ pointwiseTest smallOmegaA 1 1000+    , testCase "bigOmega"   $ pointwiseTest bigOmegaA   1 1000+    , testCase "carmichael" $ pointwiseTest carmichaelA 1 1000+    ]+  , testGroup "unboxed"+    [ testCase "id"      $ unboxedTest $ multiplicativeConfig (^)+    , testCase "tau"     $ unboxedTest $ multiplicativeConfig (const id)+    , testCase "moebius" $ unboxedTest moebiusConfig+    , testCase "totient" $ unboxedTest $ multiplicativeConfig (\p a -> (p - 1) * p ^ (a - 1))+    ]+  , testGroup "special moebius" moebiusSpecialCases+  ]
test-suite/Math/NumberTheory/ArithmeticFunctionsTests.hs view
@@ -10,6 +10,7 @@  {-# LANGUAGE CPP       #-} +{-# OPTIONS_GHC -fno-warn-deprecations  #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-}  module Math.NumberTheory.ArithmeticFunctionsTests@@ -137,21 +138,17 @@   , 1728, 1584, 2208, 1536   ] --- | moebius values are [-1, 0, 1]-moebiusProperty1 :: Natural -> Bool-moebiusProperty1 n = runFunction moebiusA n `elem` [-1, 0, 1]- -- | moebius does not require full factorisation moebiusLazy :: Assertion-moebiusLazy = assertEqual "moebius" 0 (runFunction moebiusA (2^2 * (2^100000-1) :: Natural))+moebiusLazy = assertEqual "moebius" MoebiusZ (runFunction moebiusA (2^2 * (2^100000-1) :: Natural))  -- | moebius matches baseline from OEIS. moebiusOeis :: Assertion moebiusOeis = oeisAssertion "A008683" moebiusA-  [ 1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -1-  , 0, 0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, -1, -1, -1, 0, 0, 1-  , -1, 0, 0, 0, 1, 0, -1, 0, 1, 0, 1, 1, -1, 0, -1, 1, 0, 0, 1, -1, -1, 0, 1-  , -1, -1, 0, -1, 1, 0, 0, 1+  [ MoebiusP, MoebiusN, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusN, MoebiusZ, MoebiusZ, MoebiusP, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusP, MoebiusZ, MoebiusN, MoebiusZ, MoebiusN, MoebiusZ, MoebiusP, MoebiusP, MoebiusN+  , MoebiusZ, MoebiusZ, MoebiusP, MoebiusZ, MoebiusZ, MoebiusN, MoebiusN, MoebiusN, MoebiusZ, MoebiusP, MoebiusP, MoebiusP, MoebiusZ, MoebiusN, MoebiusP, MoebiusP, MoebiusZ, MoebiusN, MoebiusN, MoebiusN, MoebiusZ, MoebiusZ, MoebiusP+  , MoebiusN, MoebiusZ, MoebiusZ, MoebiusZ, MoebiusP, MoebiusZ, MoebiusN, MoebiusZ, MoebiusP, MoebiusZ, MoebiusP, MoebiusP, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusZ, MoebiusZ, MoebiusP, MoebiusN, MoebiusN, MoebiusZ, MoebiusP+  , MoebiusN, MoebiusN, MoebiusZ, MoebiusN, MoebiusP, MoebiusZ, MoebiusZ, MoebiusP   ]  -- | liouville values are [-1, 1]@@ -160,7 +157,7 @@  -- | moebius is zero or equal to liouville liouvilleProperty2 :: Natural -> Bool-liouvilleProperty2 n = m == 0 || l == m+liouvilleProperty2 n = m == MoebiusZ || l == runMoebius m   where     l = runFunction liouvilleA n     m = runFunction moebiusA   n@@ -260,8 +257,7 @@     , testCase          "OEIS jordan_2"      jordan2Oeis     ]   , testGroup "Moebius"-    [ testSmallAndQuick "moebius values" moebiusProperty1-    , testCase          "OEIS"           moebiusOeis+    [ testCase          "OEIS"           moebiusOeis     , testCase          "Lazy"           moebiusLazy     ]   , testGroup "Liouville"
test-suite/Math/NumberTheory/GCDTests.hs view
@@ -8,6 +8,7 @@ -- Tests for Math.NumberTheory.GCD -- +{-# LANGUAGE CPP                 #-} {-# LANGUAGE ScopedTypeVariables #-}  {-# OPTIONS_GHC -fno-warn-type-defaults #-}@@ -17,8 +18,16 @@   ) where  import Test.Tasty+import Test.Tasty.HUnit +import Control.Arrow import Data.Bits+import Data.List (tails)+#if MIN_VERSION_base(4,8,0)+#else+import Data.Word+#endif+import Numeric.Natural  import Math.NumberTheory.GCD import Math.NumberTheory.TestUtils@@ -42,9 +51,53 @@ coprimeProperty :: (Integral a, Bits a) => AnySign a -> AnySign a -> Bool coprimeProperty (AnySign a) (AnySign b) = coprime a b == (gcd a b == 1) +splitIntoCoprimesProperty1 :: [(Positive Natural, Power Word)] -> Bool+splitIntoCoprimesProperty1 fs' = factorback fs == factorback (splitIntoCoprimes fs)+  where+    fs = map (getPositive *** getPower) fs'+    factorback = product . map (uncurry (^))++splitIntoCoprimesProperty2 :: [(Positive Natural, Power Word)] -> Bool+splitIntoCoprimesProperty2 fs' = multiplicities fs <= multiplicities (splitIntoCoprimes fs)+  where+    fs = map (getPositive *** getPower) fs'+    multiplicities = sum . map snd . filter ((/= 1) . fst)++splitIntoCoprimesProperty3 :: [(Positive Natural, Power Word)] -> Bool+splitIntoCoprimesProperty3 fs' = and [ coprime x y | (x : xs) <- tails fs, y <- xs ]+  where+    fs = map fst $ splitIntoCoprimes $ map (getPositive *** getPower) fs'++-- | Check that evaluation never freezes.+splitIntoCoprimesProperty4 :: [(Integer, Word)] -> Bool+splitIntoCoprimesProperty4 fs' = fs == fs+  where+    fs = splitIntoCoprimes fs'++-- | This is an undefined behaviour, but at least it should not+-- throw exceptions or loop forever.+splitIntoCoprimesSpecialCase1 :: Assertion+splitIntoCoprimesSpecialCase1 =+  assertBool "should not fail" $ splitIntoCoprimesProperty4 [(0, 0), (0, 0)]++-- | This is an undefined behaviour, but at least it should not+-- throw exceptions or loop forever.+splitIntoCoprimesSpecialCase2 :: Assertion+splitIntoCoprimesSpecialCase2 =+  assertBool "should not fail" $ splitIntoCoprimesProperty4 [(0, 1), (-2, 0)]+ testSuite :: TestTree testSuite = testGroup "GCD"   [ testSameIntegralProperty "binaryGCD"   binaryGCDProperty   , testSameIntegralProperty "extendedGCD" extendedGCDProperty   , testSameIntegralProperty "coprime"     coprimeProperty+  , testGroup "splitIntoCoprimes"+    [ testSmallAndQuick "preserves product of factors"        splitIntoCoprimesProperty1+    , testSmallAndQuick "number of factors is non-decreasing" splitIntoCoprimesProperty2+    , testSmallAndQuick "output factors are coprime"          splitIntoCoprimesProperty3++    , testCase          "does not freeze 1"                   splitIntoCoprimesSpecialCase1+    , testCase          "does not freeze 2"                   splitIntoCoprimesSpecialCase2+    , testSmallAndQuick "does not freeze random"              splitIntoCoprimesProperty4+    ]   ]
test-suite/Math/NumberTheory/GaussianIntegersTests.hs view
@@ -2,7 +2,7 @@  -- | -- Module:      Math.NumberTheory.GaussianIntegersTests--- Copyright:   (c) 2016 Chris Fredrickson+-- Copyright:   (c) 2016 Chris Fredrickson, Google Inc. -- Licence:     MIT -- Maintainer:  Chris Fredrickson <chris.p.fredrickson@gmail.com> -- Stability:   Provisional@@ -14,15 +14,25 @@   ( testSuite   ) where +import Control.Monad (zipWithM_) import Test.Tasty import Test.Tasty.HUnit  import Math.NumberTheory.GaussianIntegers import Math.NumberTheory.TestUtils +lazyCases :: [(GaussianInteger, [(GaussianInteger, Int)])]+lazyCases =+  [ ( 14145130733+    * 10000000000000000000000000000000000000121+    * 100000000000000000000000000000000000000000000000447+    , [(21037 :+ 117058, 1), (117058 :+ 21037, 1)]+    )+  ]+ -- | Number is zero or is equal to the product of its factors.-factoriseProperty :: Integer -> Integer -> Bool-factoriseProperty x y+factoriseProperty1 :: Integer -> Integer -> Bool+factoriseProperty1 x y   =  x == 0 && y == 0   || g == g'   where@@ -30,6 +40,9 @@     factors = factorise g     g' = product $ map (uncurry (.^)) factors +factoriseProperty2 :: (GaussianInteger, [(GaussianInteger, Int)]) -> Assertion+factoriseProperty2 (n, fs) = zipWithM_ (assertEqual (show n)) fs (factorise n)+ -- | Number is prime iff it is non-zero --   and has exactly one (non-unit) factor. isPrimeProperty :: Integer -> Integer -> Bool@@ -69,11 +82,13 @@ gcdGSpecialCase1 = assertEqual "gcdG" 1 $ gcdG (12 :+ 23) (23 :+ 34)  testSuite :: TestTree-testSuite = testGroup "GaussianIntegers"-  [ testSmallAndQuick "factorise"         factoriseProperty+testSuite = testGroup "GaussianIntegers" $+  [ testSmallAndQuick "factorise"         factoriseProperty1   , testSmallAndQuick "isPrime"           isPrimeProperty   , testSmallAndQuick "primes"            primesGeneratesPrimesProperty   , testSmallAndQuick "signumAbsProperty" signumAbsProperty   , testSmallAndQuick "absProperty"       absProperty   , testCase          "gcdG (12 :+ 23) (23 :+ 34)" gcdGSpecialCase1   ]+  +++  map (\x -> testCase ("laziness " ++ show (fst x)) (factoriseProperty2 x)) lazyCases
test-suite/Math/NumberTheory/Moduli/JacobiTests.hs view
@@ -20,7 +20,9 @@ import Test.Tasty  import Data.Bits+#if __GLASGOW_HASKELL__ < 803 import Data.Semigroup+#endif  import Math.NumberTheory.Moduli hiding (invertMod) import Math.NumberTheory.TestUtils
+ test-suite/Math/NumberTheory/Moduli/PrimitiveRootTests.hs view
@@ -0,0 +1,118 @@+-- |+-- Module:      Math.NumberTheory.Moduli.PrimitiveRootTests+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+--+-- Tests for Math.NumberTheory.Moduli.PrimitiveRoot+--++{-# LANGUAGE CPP                 #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -fno-warn-type-defaults #-}++module Math.NumberTheory.Moduli.PrimitiveRootTests+  ( testSuite+  ) where++import Test.Tasty++import qualified Data.Set as S+import Data.List (genericTake)+import Data.Maybe+import Numeric.Natural++#if !(MIN_VERSION_base(4,8,0))+import Data.Word+#endif++import Math.NumberTheory.ArithmeticFunctions (totient)+import Math.NumberTheory.Moduli.Class (Mod, SomeMod(..), modulo)+import Math.NumberTheory.Moduli.PrimitiveRoot+import Math.NumberTheory.Prefactored (prefValue)+import Math.NumberTheory.TestUtils+import Math.NumberTheory.UniqueFactorisation++cyclicGroupProperty1 :: (Integral a, UniqueFactorisation a, Show a) => AnySign a -> Bool+cyclicGroupProperty1 (AnySign n) = case cyclicGroupFromModulo n of+  Nothing -> True+  Just cg -> prefValue (cyclicGroupToModulo cg) == n++-- | Multiplicative groups modulo primes are always cyclic.+cyclicGroupProperty2 :: (Integral a, UniqueFactorisation a) => Positive a -> Bool+cyclicGroupProperty2 (Positive n) = case isPrime n of+  Nothing -> True+  Just _  -> isJust (cyclicGroupFromModulo n)++-- | Multiplicative groups modulo double primes are always cyclic.+cyclicGroupProperty3 :: (Integral a, UniqueFactorisation a) => Positive a -> Bool+cyclicGroupProperty3 (Positive n) = case isPrime n of+  Nothing -> True+  Just _  -> 2 * n < n {- overflow check -}+          || isJust (cyclicGroupFromModulo n)++allUnique :: Ord a => [a] -> Bool+allUnique = go S.empty+  where+    go _ []         = True+    go acc (x : xs) = if x `S.member` acc then False else go (S.insert x acc) xs++isPrimitiveRoot'Property1+  :: (Eq a, Integral a, UniqueFactorisation a)+  => AnySign a -> CyclicGroup a -> Bool+isPrimitiveRoot'Property1 (AnySign n) cg+  = gcd n (prefValue (cyclicGroupToModulo cg)) == 1+  || not (isPrimitiveRoot' cg n)++isPrimitiveRootProperty1 :: AnySign Integer -> Positive Natural -> Bool+isPrimitiveRootProperty1 (AnySign n) (Positive m)+  = case n `modulo` m of+    SomeMod n' -> gcd n (toInteger m) == 1+               || not (isPrimitiveRoot n')+    InfMod{}   -> False++isPrimitiveRootProperty2 :: Positive Natural -> Bool+isPrimitiveRootProperty2 (Positive m)+  = isNothing (cyclicGroupFromModulo m)+  || case 0 `modulo` m of+    SomeMod (_ :: Mod t) -> any isPrimitiveRoot [(minBound :: Mod t) .. maxBound]+    InfMod{}             -> False++isPrimitiveRootProperty3 :: AnySign Integer -> Positive Natural -> Bool+isPrimitiveRootProperty3 (AnySign n) (Positive m)+  = case n `modulo` m of+    SomeMod n' -> not (isPrimitiveRoot n')+               || allUnique (genericTake (totient m - 1) (iterate (* n') 1))+    InfMod{}   -> False++isPrimitiveRootProperty4 :: AnySign Integer -> Positive Natural -> Bool+isPrimitiveRootProperty4 (AnySign n) (Positive m)+  = isJust (cyclicGroupFromModulo m)+  || case n `modulo` m of+    SomeMod n' -> not (isPrimitiveRoot n')+    InfMod{}   -> False++testSuite :: TestTree+testSuite = testGroup "Primitive root"+  [ testGroup "CyclicGroup"+    [ testIntegralProperty "cyclicGroupToModulo . cyclicGroupFromModulo" cyclicGroupProperty1+    , testIntegralProperty "cyclic group mod p" cyclicGroupProperty2+    , testIntegralProperty "cyclic group mod 2p" cyclicGroupProperty3+    ]+  , testGroup "isPrimitiveRoot'"+    [ testGroup "primitive root is coprime with modulo"+      [ testSmallAndQuick "Integer" (isPrimitiveRoot'Property1 :: AnySign Integer -> CyclicGroup Integer -> Bool)+      , testSmallAndQuick "Natural" (isPrimitiveRoot'Property1 :: AnySign Natural -> CyclicGroup Natural -> Bool)+      , testSmallAndQuick "Int"     (isPrimitiveRoot'Property1 :: AnySign Int     -> CyclicGroup Int     -> Bool)+      , testSmallAndQuick "Word"    (isPrimitiveRoot'Property1 :: AnySign Word    -> CyclicGroup Word    -> Bool)+      ]+    ]+  , testGroup "isPrimitiveRoot"+    [ testSmallAndQuick "primitive root is coprime with modulo" isPrimitiveRootProperty1+    , testSmallAndQuick "cyclic group has a primitive root"     isPrimitiveRootProperty2+    , testSmallAndQuick "primitive root generates cyclic group" isPrimitiveRootProperty3+    , testSmallAndQuick "no primitive root in non-cyclic group" isPrimitiveRootProperty4+    ]+  ]
test-suite/Math/NumberTheory/Moduli/SqrtTests.hs view
@@ -21,35 +21,40 @@ import Test.Tasty.HUnit  import Data.List (nub)+import Data.Maybe (fromJust)  import Math.NumberTheory.Moduli hiding (invertMod)+import Math.NumberTheory.UniqueFactorisation (unPrime, isPrime) import Math.NumberTheory.TestUtils -unwrapPP :: (Prime, Power Int) -> (Integer, Int)-unwrapPP (Prime p, Power e) = (p, e)+unwrapP :: PrimeWrapper Integer -> Integer+unwrapP (PrimeWrapper p) = unPrime p +unwrapPP :: (PrimeWrapper Integer, Power Int) -> (Integer, Int)+unwrapPP (p, Power e) = (unwrapP p, e)+ -- | Check that 'sqrtMod' is defined iff a quadratic residue exists. --   Also check that the result is a solution of input modular equation.-sqrtModPProperty :: AnySign Integer -> Prime -> Bool-sqrtModPProperty (AnySign n) (Prime p) = case sqrtModP n p of+sqrtModPProperty :: AnySign Integer -> PrimeWrapper Integer -> Bool+sqrtModPProperty (AnySign n) (unwrapP -> p) = case sqrtModP n p of   Nothing -> jacobi n p == MinusOne   Just rt -> (p == 2 || jacobi n p /= MinusOne) && rt ^ 2 `mod` p == n `mod` p -sqrtModPListProperty :: AnySign Integer -> Prime -> Bool-sqrtModPListProperty (AnySign n) (Prime p) = all (\rt -> rt ^ 2 `mod` p == n `mod` p) (sqrtModPList n p)+sqrtModPListProperty :: AnySign Integer -> PrimeWrapper Integer -> Bool+sqrtModPListProperty (AnySign n) (unwrapP -> p) = all (\rt -> rt ^ 2 `mod` p == n `mod` p) (sqrtModPList n p) -sqrtModP'Property :: Positive Integer -> Prime -> Bool-sqrtModP'Property (Positive n) (Prime p) = (p /= 2 && jacobi n p /= One) || rt ^ 2 `mod` p == n `mod` p+sqrtModP'Property :: Positive Integer -> PrimeWrapper Integer -> Bool+sqrtModP'Property (Positive n) (unwrapP -> p) = (p /= 2 && jacobi n p /= One) || rt ^ 2 `mod` p == n `mod` p   where     rt = sqrtModP' n p -tonelliShanksProperty1 :: Positive Integer -> Prime -> Bool-tonelliShanksProperty1 (Positive n) (Prime p) = p `mod` 4 /= 1 || jacobi n p /= One || rt ^ 2 `mod` p == n `mod` p+tonelliShanksProperty1 :: Positive Integer -> PrimeWrapper Integer -> Bool+tonelliShanksProperty1 (Positive n) (unwrapP -> p) = p `mod` 4 /= 1 || jacobi n p /= One || rt ^ 2 `mod` p == n `mod` p   where     rt = tonelliShanks n p -tonelliShanksProperty2 :: Prime -> Bool-tonelliShanksProperty2 (Prime p) = p `mod` 4 /= 1 || rt ^ 2 `mod` p == n `mod` p+tonelliShanksProperty2 :: PrimeWrapper Integer -> Bool+tonelliShanksProperty2 (unwrapP -> p) = p `mod` 4 /= 1 || rt ^ 2 `mod` p == n `mod` p   where     n  = head $ filter (\s -> jacobi s p == One) [2..p-1]     rt = tonelliShanks n p@@ -61,13 +66,13 @@     ps = [17, 73, 241, 1009, 2689, 8089, 33049, 53881, 87481, 483289, 515761, 1083289, 3818929, 9257329, 22000801, 48473881, 175244281, 427733329, 898716289, 8114538721, 9176747449, 23616331489]     rts = map (\p -> tonelliShanks 2 p) ps -sqrtModPPProperty :: AnySign Integer -> (Prime, Power Int) -> Bool-sqrtModPPProperty (AnySign n) (Prime p, Power e) = gcd n p > 1 || case sqrtModPP n (p, e) of+sqrtModPPProperty :: AnySign Integer -> (PrimeWrapper Integer, Power Int) -> Bool+sqrtModPPProperty (AnySign n) (unwrapP -> p, Power e) = gcd n p > 1 || case sqrtModPP n (p, e) of   Nothing -> True   Just rt -> rt ^ 2 `mod` (p ^ e) == n `mod` (p ^ e)  sqrtModPPBase2Property :: AnySign Integer -> Power Int -> Bool-sqrtModPPBase2Property n e = sqrtModPPProperty n (Prime 2, e)+sqrtModPPBase2Property n e = sqrtModPPProperty n (PrimeWrapper $ fromJust $ isPrime (2 :: Integer), e)  sqrtModPPSpecialCase1 :: Assertion sqrtModPPSpecialCase1 =@@ -77,16 +82,16 @@ sqrtModPPSpecialCase2 =   assertEqual "sqrtModPP 16 3 2 = 4" (Just 4) (sqrtModPP 16 (3, 2)) -sqrtModPPListProperty :: AnySign Integer -> (Prime, Power Int) -> Bool-sqrtModPPListProperty (AnySign n) (Prime p, Power e) = gcd n p > 1+sqrtModPPListProperty :: AnySign Integer -> (PrimeWrapper Integer, Power Int) -> Bool+sqrtModPPListProperty (AnySign n) (unwrapP -> p, Power e) = gcd n p > 1   || all (\rt -> rt ^ 2 `mod` (p ^ e) == n `mod` (p ^ e)) (sqrtModPPList n (p, e)) -sqrtModFProperty :: AnySign Integer -> [(Prime, Power Int)] -> Bool+sqrtModFProperty :: AnySign Integer -> [(PrimeWrapper Integer, Power Int)] -> Bool sqrtModFProperty (AnySign n) (map unwrapPP -> pes) = case sqrtModF n pes of   Nothing -> True   Just rt -> all (\(p, e) -> rt ^ 2 `mod` (p ^ e) == n `mod` (p ^ e)) pes -sqrtModFListProperty :: AnySign Integer -> [(Prime, Power Int)] -> Bool+sqrtModFListProperty :: AnySign Integer -> [(PrimeWrapper Integer, Power Int)] -> Bool sqrtModFListProperty (AnySign n) (map unwrapPP -> pes)   = nub ps /= ps || all     (\rt -> all (\(p, e) -> rt ^ 2 `mod` (p ^ e) == n `mod` (p ^ e)) pes)
test-suite/Math/NumberTheory/MoebiusInversion/IntTests.hs view
@@ -37,7 +37,7 @@ generalInversionProperty :: (Int -> Int) -> Positive Int -> Bool generalInversionProperty g (Positive n)   =  g n == sum [f (n `quot` k) | k <- [1 .. n]]-  && f n == sum [fromInteger (moebius (toInteger k)) * g (n `quot` k) | k <- [1 .. n]]+  && f n == sum [runMoebius (moebius k) * g (n `quot` k) | k <- [1 .. n]]   where     f = generalInversion g 
test-suite/Math/NumberTheory/MoebiusInversionTests.hs view
@@ -37,7 +37,7 @@ generalInversionProperty :: (Int -> Integer) -> Positive Int -> Bool generalInversionProperty g (Positive n)   =  g n == sum [f (n `quot` k) | k <- [1 .. n]]-  && f n == sum [moebius (toInteger k) * g (n `quot` k) | k <- [1 .. n]]+  && f n == sum [runMoebius (moebius k) * g (n `quot` k) | k <- [1 .. n]]   where     f = generalInversion g 
+ test-suite/Math/NumberTheory/Powers/ModularTests.hs view
@@ -0,0 +1,98 @@+-- |+-- Module:      Math.NumberTheory.Powers.ModularTests+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+--+-- Tests for Math.NumberTheory.Powers.Modular+--++{-# LANGUAGE CPP #-}++{-# OPTIONS_GHC -fno-warn-type-defaults #-}++module Math.NumberTheory.Powers.ModularTests+  ( testSuite+  ) where++import Test.Tasty+#if MIN_VERSION_base(4,8,0)+import Test.Tasty.HUnit+#endif++import Numeric.Natural++import Math.NumberTheory.Powers.Modular+import Math.NumberTheory.TestUtils++#include "MachDeps.h"++powMod' :: Integer -> Natural -> Integer -> Integer+powMod' = powMod++-- | Check that 'powMod' fits between 0 and m - 1.+powModProperty1 :: NonNegative Natural -> AnySign Integer -> Positive Integer -> Bool+powModProperty1 (NonNegative e) (AnySign b) (Positive m)+  = let v = powMod' b e m in 0 <= v && v < m++-- | Check that 'powMod'' is multiplicative by first argument.+powModProperty2 :: NonNegative Natural -> AnySign Integer -> AnySign Integer -> Positive Integer -> Bool+powModProperty2 (NonNegative e) (AnySign b1) (AnySign b2) (Positive m)+  = (powMod' b1 e m * powMod' b2 e m) `mod` m == powMod' (b1 * b2) e m++-- | Check that 'powMod' is additive by second argument.+powModProperty3 :: NonNegative Natural -> NonNegative Natural -> AnySign Integer -> Positive Integer -> Bool+powModProperty3 (NonNegative e1) (NonNegative e2) (AnySign b) (Positive m)+  = (powMod' b e1 m * powMod' b e2 m) `mod` m == powMod' b (e1 + e2) m++#if __GLASGOW_HASKELL__ > 709+-- | Specialized to trigger 'powModInt'.+powModProperty_Int :: AnySign Int -> NonNegative Int -> Positive Int -> Bool+powModProperty_Int (AnySign b) (NonNegative e) (Positive m) = powModInt b e m == fromInteger (powMod' (fromIntegral b) (fromIntegral e) (fromIntegral m))++-- | Specialized to trigger 'powModWord'.+powModProperty_Word :: AnySign Word -> NonNegative Word -> Positive Word -> Bool+powModProperty_Word (AnySign b) (NonNegative e) (Positive m) = powModWord b e m == fromInteger (powMod' (fromIntegral b) (fromIntegral e) (fromIntegral m))+#endif++-- | Specialized to trigger 'powModInteger'.+powModProperty_Integer :: AnySign Integer -> NonNegative Integer -> Positive Integer -> Bool+powModProperty_Integer (AnySign b) (NonNegative e) (Positive m) = powMod b e m == fromInteger (powMod' (fromIntegral b) (fromIntegral e) (fromIntegral m))++-- | Specialized to trigger 'powModNatural'.+powModProperty_Natural :: AnySign Natural -> NonNegative Natural -> Positive Natural -> Bool+powModProperty_Natural (AnySign b) (NonNegative e) (Positive m) = powMod b e m == fromInteger (powMod' (fromIntegral b) (fromIntegral e) (fromIntegral m))++#if WORD_SIZE_IN_BITS == 64 && __GLASGOW_HASKELL__ > 709+-- | Large modulo m such that m^2 overflows.+powModSpecialCase1_Int :: Assertion+powModSpecialCase1_Int =+  assertEqual "powModInt" (powModInt 3 101 (2^60-1)) 1018105167100379328++-- | Large modulo m such that m^2 overflows.+powModSpecialCase1_Word :: Assertion+powModSpecialCase1_Word =+  assertEqual "powModWord" (powModWord 3 101 (2^60-1)) 1018105167100379328+#endif++testSuite :: TestTree+testSuite = testGroup "Modular"+  [ testGroup "powMod"+    [ testSmallAndQuick "range"                  powModProperty1+    , testSmallAndQuick "multiplicative by base" powModProperty2+    , testSmallAndQuick "additive by exponent"   powModProperty3++#if __GLASGOW_HASKELL__ > 709+    , testSmallAndQuick "powModInt"              powModProperty_Int+    , testSmallAndQuick "powModWord"             powModProperty_Word+#endif+    , testSmallAndQuick "powModInteger"          powModProperty_Integer+    , testSmallAndQuick "powModNatural"          powModProperty_Natural++#if WORD_SIZE_IN_BITS == 64 && __GLASGOW_HASKELL__ > 709+    , testCase          "large modulo :: Int"    powModSpecialCase1_Int+    , testCase          "large modulo :: Word"   powModSpecialCase1_Word+#endif+    ]+  ]
+ test-suite/Math/NumberTheory/PrefactoredTests.hs view
@@ -0,0 +1,101 @@+-- |+-- Module:      Math.NumberTheory.PrefactoredTests+-- Copyright:   (c) 2017 Andrew Lelechenko+-- Licence:     MIT+-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>+-- Stability:   Provisional+--+-- Tests for Math.NumberTheory.Prefactored+--++{-# LANGUAGE CPP #-}++{-# OPTIONS_GHC -fno-warn-type-defaults #-}++module Math.NumberTheory.PrefactoredTests+  ( testSuite+  ) where++import Test.Tasty++import Control.Arrow (second)+import Data.Bits (Bits)+import Data.List (tails)+#if MIN_VERSION_base(4,8,0)+#else+import Data.Word+#endif+import Numeric.Natural++import Math.NumberTheory.GCD (coprime, splitIntoCoprimes)+import Math.NumberTheory.Prefactored+import Math.NumberTheory.TestUtils++isValid :: (Eq a, Bits a, Integral a) => Prefactored a -> Bool+isValid pref+  = abs n == abs (product (map (uncurry (^)) fs))+  && and [ coprime g h | ((g, _) : gs) <- tails fs, (h, _) <- gs ]+  where+    n  = prefValue   pref+    fs = prefFactors pref++fromValueProperty :: Integer -> Bool+fromValueProperty n = isValid pref && prefValue pref == n+  where+    pref = fromValue n++fromFactorsProperty :: [(Integer, Power Word)] -> Bool+fromFactorsProperty fs' = isValid pref && abs (prefValue pref) == abs (product (map (uncurry (^)) fs))+  where+    fs   = map (second getPower) fs'+    pref = fromFactors (splitIntoCoprimes fs)++plusProperty :: Integer -> Integer -> Bool+plusProperty x y = isValid z && prefValue z == x + y+  where+    z = fromValue x + fromValue y++minusProperty :: Integer -> Integer -> Bool+minusProperty x y = isValid z && prefValue z == x - y+  where+    z = fromValue x - fromValue y++minusNaturalProperty :: Natural -> Natural -> Bool+minusNaturalProperty x y = x < y || (isValid z && prefValue z == x - y)+  where+    z = fromValue x - fromValue y++multiplyProperty :: Integer -> Integer -> Bool+multiplyProperty x y = isValid z && prefValue z == x * y+  where+    z = fromValue x * fromValue y++negateProperty :: Integer -> Bool+negateProperty x = isValid z && prefValue z == negate x+  where+    z = negate (fromValue x)++absSignumProperty :: Integer -> Bool+absSignumProperty x = isValid z && prefValue z == x+  where+    z = abs (fromValue x) * signum (fromValue x)++fromIntegerProperty :: Integer -> Bool+fromIntegerProperty n = isValid pref && prefValue pref == n+  where+    pref = fromInteger n++testSuite :: TestTree+testSuite = testGroup "Prefactored"+  [ testSmallAndQuick "fromValue"   fromValueProperty+  , testSmallAndQuick "fromFactors" fromFactorsProperty+  , testGroup "Num instance"+    [ testSmallAndQuick "plus"         plusProperty+    , testSmallAndQuick "minus"        minusProperty+    , testSmallAndQuick "minusNatural" minusNaturalProperty+    , testSmallAndQuick "multiply"     multiplyProperty+    , testSmallAndQuick "negate"       negateProperty+    , testSmallAndQuick "absSignum"    absSignumProperty+    , testSmallAndQuick "fromInteger"  fromIntegerProperty+    ]+  ]
test-suite/Math/NumberTheory/Primes/FactorisationTests.hs view
@@ -17,6 +17,7 @@ import Test.Tasty import Test.Tasty.HUnit +import Control.Monad (zipWithM_) import Data.List (nub, sort)  import Math.NumberTheory.Primes.Factorisation@@ -46,6 +47,15 @@   , (16757651897802863152387219654541878166,[(2,1),(23,1),(277,1),(505353699591289,1),(2602436338718275457,1)])   ] +lazyCases :: [(Integer, [(Integer, Int)])]+lazyCases =+  [ ( 14145130711+    * 10000000000000000000000000000000000000121+    * 100000000000000000000000000000000000000000000000447+    , [(14145130711, 1)]+    )+  ]+ factoriseProperty1 :: Assertion factoriseProperty1 = assertEqual "0" [] (factorise 1) @@ -64,8 +74,11 @@ factoriseProperty5 (Positive n) = product (map (uncurry (^)) (factorise n)) == n  factoriseProperty6 :: (Integer, [(Integer, Int)]) -> Assertion-factoriseProperty6 (n, fs) = assertEqual (show n) fs (factorise n)+factoriseProperty6 (n, fs) = assertEqual (show n) (sort fs) (sort (factorise n)) +factoriseProperty7 :: (Integer, [(Integer, Int)]) -> Assertion+factoriseProperty7 (n, fs) = zipWithM_ (assertEqual (show n)) fs (factorise n)+ testSuite :: TestTree testSuite = testGroup "Factorisation"   [ testGroup "factorise" $@@ -76,4 +89,6 @@     , testSmallAndQuick "factorback"                     factoriseProperty5     ] ++     map (\x -> testCase ("special case " ++ show (fst x)) (factoriseProperty6 x)) specialCases+    +++    map (\x -> testCase ("laziness " ++ show (fst x)) (factoriseProperty7 x)) lazyCases   ]
− test-suite/Math/NumberTheory/Primes/HeapTests.hs
@@ -1,67 +0,0 @@--- |--- Module:      Math.NumberTheory.Primes.HeapTests--- Copyright:   (c) 2016 Andrew Lelechenko--- Licence:     MIT--- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>--- Stability:   Provisional------ Tests for Math.NumberTheory.Primes.Heap-----{-# LANGUAGE CPP #-}--{-# OPTIONS_GHC -fno-warn-type-defaults #-}--module Math.NumberTheory.Primes.HeapTests-  ( testSuite-  ) where--import Prelude hiding (words)--import Test.Tasty-import Test.Tasty.HUnit--#if MIN_VERSION_base(4,8,0)-#else-import Data.Word-#endif--import Math.NumberTheory.Primes.Heap-import Math.NumberTheory.Primes.Testing-import Math.NumberTheory.TestUtils---- | Check that 'primes' over different integral types matches with 'isPrime'.-primesProperty1 :: Assertion-primesProperty1 = do-  assertEqual "ints  == integers" (trim ints)  (trim integers)-  assertEqual "words == integers" (trim words) (trim integers)-  assertEqual "naive == integers" (trim naive) (trim integers)-  where-    trim :: Integral a => [a] -> [Integer]-    trim = map toInteger . take 100000--    ints     = primes :: [Int]-    words    = primes :: [Word]-    integers = primes :: [Integer]-    naive    = filter isPrime [1..] :: [Integer]---- | Check that 'sieveFrom' over different integral types matches with 'isPrime'.-sieveFromProperty1 :: NonNegative Integer -> Bool-sieveFromProperty1 (NonNegative lowBound)-  =  trim ints  == trim integers-  && trim words == trim integers-  && trim naive == trim integers-  where-    trim :: Integral a => [a] -> [Integer]-    trim = map toInteger . take 1000--    ints     = sieveFrom (fromInteger lowBound) :: [Int]-    words    = sieveFrom (fromInteger lowBound) :: [Word]-    integers = sieveFrom lowBound               :: [Integer]-    naive    = filter isPrime [lowBound..]      :: [Integer]--testSuite :: TestTree-testSuite = testGroup "Heap"-  [ testCase          "primes"    primesProperty1-  , testSmallAndQuick "sieveFrom" sieveFromProperty1-  ]
test-suite/Math/NumberTheory/Primes/SieveTests.hs view
@@ -11,6 +11,7 @@ {-# LANGUAGE CPP #-}  {-# OPTIONS_GHC -fno-warn-type-defaults #-}+{-# OPTIONS_GHC -fno-warn-deprecations  #-}  module Math.NumberTheory.Primes.SieveTests   ( testSuite
test-suite/Math/NumberTheory/PrimesTests.hs view
@@ -8,6 +8,7 @@ -- Tests for Math.NumberTheory.Primes -- +{-# OPTIONS_GHC -fno-warn-deprecations  #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-}  module Math.NumberTheory.PrimesTests
test-suite/Math/NumberTheory/TestUtils.hs view
@@ -47,7 +47,7 @@ import Test.Tasty.SmallCheck as SC import Test.Tasty.QuickCheck as QC hiding (Positive, NonNegative, generate, getNonNegative) -import Test.SmallCheck.Series (Positive(..), NonNegative(..), Serial(..), Series, generate)+import Test.SmallCheck.Series (Positive(..), NonNegative(..), Serial(..), Series, generate, (\/))  #if !(MIN_VERSION_base(4,8,0)) import Control.Applicative@@ -59,6 +59,8 @@ import Numeric.Natural  import Math.NumberTheory.GaussianIntegers (GaussianInteger(..))+import Math.NumberTheory.Moduli.PrimitiveRoot (CyclicGroup(..))+import Math.NumberTheory.UniqueFactorisation (UniqueFactorisation, Prime, unPrime)  import Math.NumberTheory.TestUtils.MyCompose import Math.NumberTheory.TestUtils.Wrappers@@ -88,6 +90,39 @@ instance Monad m => Serial m GaussianInteger where   series = cons2 (:+) +-------------------------------------------------------------------------------+-- Cyclic group++instance (Eq a, Num a, UniqueFactorisation a, Arbitrary a) => Arbitrary (CyclicGroup a) where+  arbitrary = frequency+    [ (1, pure CG2)+    , (1, pure CG4)+    , (9, CGOddPrimePower+      <$> (arbitrary :: Gen (PrimeWrapper a)) `suchThatMap` isOddPrime+      <*> (getPower <$> arbitrary))+    , (9, CGDoubleOddPrimePower+      <$> (arbitrary :: Gen (PrimeWrapper a)) `suchThatMap` isOddPrime+      <*> (getPower <$> arbitrary))+    ]++instance (Monad m, Eq a, Num a, UniqueFactorisation a, Serial m a) => Serial m (CyclicGroup a) where+  series = pure CG2+        \/ pure CG4+        \/ (CGOddPrimePower+           <$> (series :: Series m (PrimeWrapper a)) `suchThatMapSerial` isOddPrime+           <*> (getPower <$> series))+        \/ (CGDoubleOddPrimePower+           <$> (series :: Series m (PrimeWrapper a)) `suchThatMapSerial` isOddPrime+           <*> (getPower <$> series))++isOddPrime+  :: forall a. (Eq a, Num a, UniqueFactorisation a)+  => PrimeWrapper a+  -> Maybe (Prime a)+isOddPrime (PrimeWrapper p) = if (unPrime p :: a) == 2 then Nothing else Just p++-------------------------------------------------------------------------------+ -- https://www.cs.ox.ac.uk/projects/utgp/school/andres.pdf, p. 21 -- :k Compose = (k1 -> Constraint) -> (k2 -> k1) -> (k2 -> Constraint) class    (f (g x)) => (f `Compose` g) x@@ -114,7 +149,7 @@  testIntegralProperty   :: forall wrapper bool. (TestableIntegral wrapper, SC.Testable IO bool, QC.Testable bool)-  => String -> (forall a. (Integral a, Bits a) => wrapper a -> bool) -> TestTree+  => String -> (forall a. (Integral a, Bits a, UniqueFactorisation a, Show a) => wrapper a -> bool) -> TestTree testIntegralProperty name f = testGroup name   [ SC.testProperty "smallcheck Int"     (f :: wrapper Int     -> bool)   , SC.testProperty "smallcheck Word"    (f :: wrapper Word    -> bool)@@ -129,7 +164,7 @@  testSameIntegralProperty   :: forall wrapper1 wrapper2 bool. (TestableIntegral wrapper1, TestableIntegral wrapper2, SC.Testable IO bool, QC.Testable bool)-  => String -> (forall a. (Integral a, Bits a) => wrapper1 a -> wrapper2 a -> bool) -> TestTree+  => String -> (forall a. (Integral a, Bits a, UniqueFactorisation a, Show a) => wrapper1 a -> wrapper2 a -> bool) -> TestTree testSameIntegralProperty name f = testGroup name   [ SC.testProperty "smallcheck Int"     (f :: wrapper1 Int     -> wrapper2 Int     -> bool)   , SC.testProperty "smallcheck Word"    (f :: wrapper1 Word    -> wrapper2 Word    -> bool)@@ -144,7 +179,7 @@  testIntegral2Property   :: forall wrapper1 wrapper2 bool. (TestableIntegral wrapper1, TestableIntegral wrapper2, SC.Testable IO bool, QC.Testable bool)-  => String -> (forall a1 a2. (Integral a1, Integral a2, Bits a1, Bits a2) => wrapper1 a1 -> wrapper2 a2 -> bool) -> TestTree+  => String -> (forall a1 a2. (Integral a1, Integral a2, Bits a1, Bits a2, UniqueFactorisation a1, UniqueFactorisation a2, Show a1, Show a2) => wrapper1 a1 -> wrapper2 a2 -> bool) -> TestTree testIntegral2Property name f = testGroup name   [ SC.testProperty "smallcheck Int Int"         (f :: wrapper1 Int     -> wrapper2 Int     -> bool)   , SC.testProperty "smallcheck Int Word"        (f :: wrapper1 Int     -> wrapper2 Word    -> bool)
test-suite/Math/NumberTheory/TestUtils/Wrappers.hs view
@@ -13,11 +13,14 @@ {-# LANGUAGE DeriveFoldable             #-} {-# LANGUAGE DeriveFunctor              #-} {-# LANGUAGE DeriveTraversable          #-}+{-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-} {-# LANGUAGE StandaloneDeriving         #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE UndecidableInstances       #-}  {-# OPTIONS_GHC -fno-warn-orphans       #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-}@@ -35,7 +38,7 @@ import Test.Tasty.QuickCheck as QC hiding (Positive, NonNegative, generate, getNonNegative, getPositive) import Test.SmallCheck.Series (Positive(..), NonNegative(..), Serial(..), Series) -import Math.NumberTheory.Primes (isPrime, nthPrime)+import Math.NumberTheory.UniqueFactorisation  ------------------------------------------------------------------------------- -- AnySign@@ -167,19 +170,40 @@ ------------------------------------------------------------------------------- -- Prime -newtype Prime = Prime { getPrime :: Integer }-  deriving (Eq, Ord, Show)+newtype PrimeWrapper a = PrimeWrapper { getPrime :: Prime a } -instance Arbitrary Prime where-  arbitrary = do-    n <- arbitrary-    return $ Prime $ head $ filter isPrime [abs n ..]+deriving instance Eq   (Prime a) => Eq   (PrimeWrapper a)+deriving instance Ord  (Prime a) => Ord  (PrimeWrapper a)+deriving instance Show (Prime a) => Show (PrimeWrapper a) -instance Monad m => Serial m Prime where-  series = Prime . nthPrime <$> series `suchThatSerial` (> 0)+instance (Arbitrary a, UniqueFactorisation a) => Arbitrary (PrimeWrapper a) where+  arbitrary = PrimeWrapper <$> (arbitrary :: Gen a) `suchThatMap` isPrime +instance (Monad m, Serial m a, UniqueFactorisation a) => Serial m (PrimeWrapper a) where+  series = PrimeWrapper <$> (series :: Series m a) `suchThatMapSerial` isPrime+ -------------------------------------------------------------------------------+-- UniqueFactorisation++type instance Prime (Large a) = Prime a++instance UniqueFactorisation a => UniqueFactorisation (Large a) where+  unPrime p = Large (unPrime p)+  factorise (Large x) = factorise x+  isPrime (Large x) = isPrime x++type instance Prime (Huge a) = Prime a++instance UniqueFactorisation a => UniqueFactorisation (Huge a) where+  unPrime p = Huge (unPrime p)+  factorise (Huge x) = factorise x+  isPrime (Huge x) = isPrime x++------------------------------------------------------------------------------- -- Utils  suchThatSerial :: Series m a -> (a -> Bool) -> Series m a suchThatSerial s p = s >>= \x -> if p x then pure x else empty++suchThatMapSerial :: Series m a -> (a -> Maybe b) -> Series m b+suchThatMapSerial s p = s >>= maybe empty pure . p
test-suite/Math/NumberTheory/UniqueFactorisationTests.hs view
@@ -26,7 +26,7 @@  import Math.NumberTheory.GaussianIntegers hiding (factorise) import Math.NumberTheory.UniqueFactorisation-import Math.NumberTheory.TestUtils hiding (Prime)+import Math.NumberTheory.TestUtils  import Numeric.Natural 
test-suite/Test.hs view
@@ -9,6 +9,7 @@ import qualified Math.NumberTheory.Moduli.ChineseTests as ModuliChinese import qualified Math.NumberTheory.Moduli.ClassTests as ModuliClass import qualified Math.NumberTheory.Moduli.JacobiTests as ModuliJacobi+import qualified Math.NumberTheory.Moduli.PrimitiveRootTests as ModuliPrimitiveRoot import qualified Math.NumberTheory.Moduli.SqrtTests as ModuliSqrt  import qualified Math.NumberTheory.MoebiusInversionTests as MoebiusInversion@@ -17,18 +18,22 @@ import qualified Math.NumberTheory.Powers.CubesTests as Cubes import qualified Math.NumberTheory.Powers.FourthTests as Fourth import qualified Math.NumberTheory.Powers.GeneralTests as General+import qualified Math.NumberTheory.Powers.ModularTests as Modular import qualified Math.NumberTheory.Powers.SquaresTests as Squares +import qualified Math.NumberTheory.PrefactoredTests as Prefactored+ import qualified Math.NumberTheory.PrimesTests as Primes import qualified Math.NumberTheory.Primes.CountingTests as Counting import qualified Math.NumberTheory.Primes.FactorisationTests as Factorisation-import qualified Math.NumberTheory.Primes.HeapTests as Heap import qualified Math.NumberTheory.Primes.SieveTests as Sieve import qualified Math.NumberTheory.Primes.TestingTests as Testing  import qualified Math.NumberTheory.GaussianIntegersTests as Gaussian  import qualified Math.NumberTheory.ArithmeticFunctionsTests as ArithmeticFunctions+import qualified Math.NumberTheory.ArithmeticFunctions.MertensTests as Mertens+import qualified Math.NumberTheory.ArithmeticFunctions.SieveBlockTests as SieveBlock import qualified Math.NumberTheory.UniqueFactorisationTests as UniqueFactorisation import qualified Math.NumberTheory.ZetaTests as Zeta import qualified Math.NumberTheory.CurvesTests as Curves@@ -42,6 +47,7 @@     [ Cubes.testSuite     , Fourth.testSuite     , General.testSuite+    , Modular.testSuite     , Squares.testSuite     ]   , testGroup "GCD"@@ -56,17 +62,20 @@     [ ModuliChinese.testSuite     , ModuliClass.testSuite     , ModuliJacobi.testSuite+    , ModuliPrimitiveRoot.testSuite     , ModuliSqrt.testSuite     ]   , testGroup "MoebiusInversion"     [ MoebiusInversion.testSuite     , MoebiusInversionInt.testSuite     ]+  , testGroup "Prefactored"+    [ Prefactored.testSuite+    ]   , testGroup "Primes"     [ Primes.testSuite     , Counting.testSuite     , Factorisation.testSuite-    , Heap.testSuite     , Sieve.testSuite     , Testing.testSuite     ]@@ -75,6 +84,8 @@     ]   , testGroup "ArithmeticFunctions"     [ ArithmeticFunctions.testSuite+    , Mertens.testSuite+    , SieveBlock.testSuite     ]   , testGroup "UniqueFactorisation"     [ UniqueFactorisation.testSuite