packages feed

arithmoi 0.2.0.6 → 0.3.0.0

raw patch · 4 files changed

+217/−4 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Math.NumberTheory.Moduli: chineseRemainder :: [(Integer, Integer)] -> Maybe Integer
+ Math.NumberTheory.Moduli: chineseRemainder2 :: (Integer, Integer) -> (Integer, Integer) -> Integer
+ Math.NumberTheory.Moduli: sqrtModF :: Integer -> [(Integer, Int)] -> Maybe Integer
+ Math.NumberTheory.Moduli: sqrtModFList :: Integer -> [(Integer, Int)] -> [Integer]
+ Math.NumberTheory.Moduli: sqrtModP :: Integer -> Integer -> Maybe Integer
+ Math.NumberTheory.Moduli: sqrtModP' :: Integer -> Integer -> Integer
+ Math.NumberTheory.Moduli: sqrtModPList :: Integer -> Integer -> [Integer]
+ Math.NumberTheory.Moduli: sqrtModPP :: Integer -> (Integer, Int) -> Maybe Integer
+ Math.NumberTheory.Moduli: sqrtModPPList :: Integer -> (Integer, Int) -> [Integer]
+ Math.NumberTheory.Moduli: tonelliShanks :: Integer -> Integer -> Integer

Files

Changes view
@@ -1,3 +1,5 @@+0.3.0.0:+    Added modular square roots and Chinese remainder theorem 0.2.0.6:     Performance tweaks for powerModInteger (~10%) and     invertMod (~25%).
Math/NumberTheory/Moduli.hs view
@@ -15,10 +15,21 @@     , invertMod     , powerMod     , powerModInteger+    , chineseRemainder+      -- ** Partially checked input+    , sqrtModP       -- * Unchecked functions     , jacobi'     , powerMod'     , powerModInteger'+    , sqrtModPList+    , sqrtModP'+    , tonelliShanks+    , sqrtModPP+    , sqrtModPPList+    , sqrtModF+    , sqrtModFList+    , chineseRemainder2     ) where  #include "MachDeps.h"@@ -27,8 +38,15 @@ import Data.Bits import Data.Array.Unboxed import Data.Array.Base (unsafeAt)+import Data.Maybe (fromJust)+import Data.List (nub)+import Control.Monad (foldM, liftM2) -import Math.NumberTheory.Utils (shiftToOddCount)+import Math.NumberTheory.Utils (shiftToOddCount, splitOff)+import Math.NumberTheory.GCD (extendedGCD)+import Math.NumberTheory.Primes.Heap (sieveFrom)+-- Guesstimated startup time for the Heap algorithm is lower than+-- the cost to sieve an entire chunk.  -- | Invert a number relative to a modulus. --   If @number@ and @modulus@ are coprime, the result is@@ -266,6 +284,187 @@  #endif +-- | @sqrtModP n prime@ calculates a modular square root of @n@ modulo @prime@+--   if that exists. The second argument /must/ be a (positive) prime, otherwise+--   the computation may not terminate and if it does, may yield a wrong result.+--   The precondition is /not/ checked.+--+--   If @prime@ is a prime and @n@ a quadratic residue modulo @prime@, the result+--   is @Just r@ where @r^2 ≡ n (mod prime)@, if @n@ is a quadratic nonresidue,+--   the result is @Nothing@.+sqrtModP :: Integer -> Integer -> Maybe Integer+sqrtModP n 2 = Just (n `mod` 2)+sqrtModP n prime = case jacobi' n prime of+                     0 -> Just 0+                     1 -> Just (tonelliShanks (n `mod` prime) prime)+                     _ -> Nothing++-- | @sqrtModPList n prime@ computes the list of all square roots of @n@+--   modulo @prime@. @prime@ /must/ be a (positive) prime.+--   The precondition is /not/ checked.+sqrtModPList :: Integer -> Integer -> [Integer]+sqrtModPList n prime+    | prime == 2    = [n `mod` 2]+    | otherwise     = case sqrtModP n prime of+                        Just 0 -> [0]+                        Just r -> [r,prime-r] -- The group of units in Z/(p) is cyclic+                        _      -> []++-- | @sqrtModP' square prime@ finds a square root of @square@ modulo+--   prime. @prime@ /must/ be a (positive) prime, and @sqaure@ /must/ be a+--   quadratic residue modulo @prime@, i.e. @'jacobi square prime == 1@.+--   The precondition is /not/ checked.+sqrtModP' :: Integer -> Integer -> Integer+sqrtModP' square prime+    | prime == 2    = square+    | rem4 prime == 3 = powerModInteger' square ((prime + 1) `quot` 4) prime+    | otherwise     = tonelliShanks square prime++-- | @tonelliShanks square prime@ calculates a square root of @square@+--   modulo @prime@, where @prime@ is a prime of the form @4*k + 1@ and+--   @square@ is a quadratic residue modulo @prime@, using the+--   Tonelli-Shanks algorithm.+--   No checks on the input are performed.+tonelliShanks :: Integer -> Integer -> Integer+tonelliShanks square prime = loop rc t1 generator log2+  where+    (log2,q) = shiftToOddCount (prime-1)+    nonSquare = findNonSquare prime+    generator = powerModInteger' nonSquare q prime+    rc = powerModInteger' square ((q+1) `quot` 2) prime+    t1 = powerModInteger' square q prime+    msqr x = (x*x) `rem` prime+    msquare 0 x = x+    msquare k x = msquare (k-1) (msqr x)+    findPeriod per 1 = per+    findPeriod per x = findPeriod (per+1) (msqr x)+    loop !r t c m+        | t == 1    = r+        | otherwise = loop nextR nextT nextC nextM+          where+            nextM = findPeriod 0 t+            b     = msquare (m - 1 - nextM) c+            nextR = (r*b) `rem` prime+            nextC = msqr b+            nextT = (t*nextC) `rem` prime++-- | @sqrtModPP n (prime,expo)@ calculates a square root of @n@+--   modulo @prime^expo@ if one exists. @prime@ /must/ be a+--   (positive) prime. @expo@ must be positive, @n@ must be coprime+--   to @prime@+sqrtModPP :: Integer -> (Integer,Int) -> Maybe Integer+sqrtModPP n (2,e) = sqM2P n e+sqrtModPP n (prime,expo) = case sqrtModP n prime of+                             Just r -> Just $ fixup r+                             _      -> Nothing+  where+    fixup r = case splitOff prime (r*r-n) of+                (e,q) | expo <= e -> r+                      | otherwise -> hoist (fromJust $ invertMod (2*r) prime) r (q `mod` prime) (prime^e)+                      --+    hoist inv root elim pp+        | expo <= ex    = root'+        | otherwise     = hoist inv root' (nelim `mod` prime) (prime^ex)+          where+            root' = (root + (inv*(prime-elim))*pp) `mod` (prime*pp)+            (ex, nelim) = splitOff prime (root'*root' - n)++-- dirty, dirty+sqM2P :: Integer -> Int -> Maybe Integer+sqM2P n e+    | e < 2     = Just (n `mod` 2)+    | n' == 0   = Just 0+    | e <= k    = Just 0+    | odd k     = Nothing+    | otherwise = fmap ((`mod` mdl) . (`shiftL` k2)) $ solve s e2+      where+        mdl = 1 `shiftL` e+        n' = n `mod` mdl+        (k,s) = shiftToOddCount n'+        k2 = k `quot` 2+        e2 = e-k+        solve _ 1 = Just 1+        solve 1 _ = Just 1+        solve r p+            | rem4 r == 3   = Nothing  -- otherwise r ≡ 1 (mod 4)+            | p == 2        = Just 1   -- otherwise p >= 3+            | rem8 r == 5   = Nothing  -- otherwise r ≡ 1 (mod 8)+            | otherwise     = fixup r (fst $ shiftToOddCount (r-1))+              where+                fixup x pw+                    | pw >= e2  = Just x+                    | otherwise = fixup x' pw'+                      where+                        x' = x + (1 `shiftL` (pw-1))+                        d = x'*x' - r+                        pw' = if d == 0 then e2 else fst (shiftToOddCount d)++-- | @sqrtModF n primePowers@ calculates a square root of @n@ modulo+--   @product [p^k | (p,k) <- primePowers]@ if one exists and all primes+--   are distinct.+sqrtModF :: Integer -> [(Integer,Int)] -> Maybe Integer+sqrtModF n pps = do roots <- mapM (sqrtModPP n) pps+                    chineseRemainder $ zip roots (map (uncurry (^)) pps)++-- | @sqrtModFList n primePowers@ calculates all square roots of @n@ modulo+--   @product [p^k | (p,k) <- primePowers]@ if all primes are distinct.+sqrtModFList :: Integer -> [(Integer,Int)] -> [Integer]+sqrtModFList n pps = map fst $ foldl1 (liftM2 comb) cs+  where+    ms :: [Integer]+    ms = map (uncurry (^)) pps+    rs :: [[Integer]]+    rs = map (sqrtModPPList n) pps+    cs :: [[(Integer,Integer)]]+    cs = zipWith (\l m -> map (\x -> (x,m)) l) rs ms+    comb t1@(_,m1) t2@(_,m2) = (chineseRemainder2 t1 t2,m1*m2)++-- | @sqrtModPPList n (prime,expo)@ calculates the list of all+--   square roots of @n@ modulo @prime^expo@. The same restriction+--   as in 'sqrtModPP' applies to the arguments.+sqrtModPPList :: Integer -> (Integer,Int) -> [Integer]+sqrtModPPList n (2,expo)+    = case sqM2P n expo of+        Just r -> let m = 1 `shiftL` (expo-1)+                  in nub [r, (r+m) `mod` (2*m), (m-r) `mod` (2*m), 2*m-r]+        _ -> []+sqrtModPPList n pe@(prime,expo)+    = case sqrtModPP n pe of+        Just 0 -> [0]+        Just r -> [prime^expo - r, r] -- The group of units in Z/(p^e) is cyclic+        _      -> []++-- | Given a list @[(r_1,m_1), ..., (r_n,m_n)]@ of @(residue,modulus)@+--   pairs, @chineseRemainder@ calculates the solution to the simultaneous+--   congruences+--+-- >+-- > r ≡ r_k (mod m_k)+-- >+--+--   if all moduli are pairwise coprime. If not all moduli are+--   pairwise coprime, the result is @Nothing@ regardless of whether+--   a solution exists.+chineseRemainder :: [(Integer,Integer)] -> Maybe Integer+chineseRemainder remainders = foldM addRem 0 remainders+  where+    !modulus = product (map snd remainders)+    addRem acc (r,m) = do+        let cf = modulus `quot` m+        inv <- invertMod cf m+        Just $! (acc + inv*cf*r) `mod` modulus++-- | @chineseRemainder2 (r_1,m_1) (r_2,m_2)@ calculates the solution of+--+-- >+-- > r ≡ r_k (mod m_k)+--+--   if @m_1@ and @m_2@ are coprime.+chineseRemainder2 :: (Integer,Integer) -> (Integer,Integer) -> Integer+chineseRemainder2 (r1, md1) (r2,md2)+    = case extendedGCD md1 md2 of+        (_,u,v) -> ((1 - u*md1)*r1 + (1 - v*md2)*r2) `mod` (md1*md2)+ -- Utilities  -- For large Integers, going via Int is much faster than bit-fiddling@@ -293,3 +492,15 @@  jac2 :: UArray Int Int jac2 = array (0,7) [(0,0),(1,1),(2,0),(3,-1),(4,0),(5,-1),(6,0),(7,1)]++findNonSquare :: Integer -> Integer+findNonSquare n+    | rem8 n == 5 || rem8 n == 3  = 2+    | otherwise = search primelist+      where+        primelist = [3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67]+                        ++ sieveFrom (68 + n `rem` 4) -- prevent sharing+        search (p:ps)+            | jacobi' p n == -1 = p+            | otherwise         = search ps+        search _ = error "Should never have happened, prime list exhausted."
Math/NumberTheory/Primes/Sieve/Eratosthenes.hs view
@@ -10,7 +10,7 @@ -- {-# LANGUAGE CPP, BangPatterns #-} #if __GLASGOW_HASKELL__ >= 700-{-# OPTIONS_GHC -fspec-constr-count=6 #-}+{-# OPTIONS_GHC -fspec-constr-count=8 #-} #endif {-# OPTIONS_HADDOCK hide #-} module Math.NumberTheory.Primes.Sieve.Eratosthenes
arithmoi.cabal view
@@ -1,5 +1,5 @@ name                : arithmoi-version             : 0.2.0.6+version             : 0.3.0.0 cabal-version       : >= 1.6 author              : Daniel Fischer copyright           : (c) 2011 Daniel Fischer@@ -27,7 +27,7 @@  category            : Math, Algorithms, Number Theory -tested-with         : GHC == 6.12.3, GHC == 7.0.2, GHC == 7.0.4, GHC == 7.2.1+tested-with         : GHC == 6.12.3, GHC == 7.0.2, GHC == 7.0.4, GHC == 7.2.1, GHC == 7.4.1  extra-source-files  : Changes, TODO