primecount-0.1.0.0: src/Math/NumberTheory/Prime/Count.hs
-- |
-- Module : Math.NumberTheory.Prime.Count
-- Copyright : 2021 Preetham Gujjula
-- License : BSD-Clause
-- Maintainer : primecount-haskell@mail.preetham.io
-- Stability : experimental
--
-- This module provides a high-level, polymorphic interface to the primecount
-- library. For a lower-level interface, see
-- "Math.NumberTheory.Prime.Count.FFI#".
module Math.NumberTheory.Prime.Count
( primePi,
primePiMaxBound,
nthPrime,
nthPrimeMaxBound,
primePhi,
getNumPrimecountThreads,
setNumPrimecountThreads,
primecountVersion,
)
where
import Data.Int (Int64)
import Foreign.C.String (CString, peekCString, withCString)
import Foreign.C.Types (CSize)
import Foreign.Marshal.Array (allocaArray)
import Math.NumberTheory.Prime.Count.FFI
import System.IO.Unsafe (unsafePerformIO)
import Text.Read (readMaybe)
-- | The number of primes less than or equal to @n@. Throws an error if @n@
-- is larger than 'primePiMaxBound'. Also might throw an error if there's not
-- enough memory available to compute the result, which can happen even if @n@
-- is smaller than @primePiMaxBound@.
primePi :: Integral a => a -> a
primePi n
| n < 0 = 0
| n' <= bound = fromIntegral (primecount_pi (fromInteger n'))
| n' <= primePiMaxBound = fromInteger (primePiStr n')
| otherwise = error "primePi: input larger than primePiMaxBound"
where
bound :: Integer
bound = toInteger (maxBound :: Int64)
n' :: Integer
n' = toInteger n
-- | The largest input supported by 'primePi'.
--
-- * 64-bit CPUs: @10^31@
-- * 32-bit CPUs: @2^63 - 1@
primePiMaxBound :: Integer
primePiMaxBound = read $ unsafePerformIO $ peekCString primecount_get_max_x
{-# NOINLINE primePiMaxBound #-}
primePiStr :: Integer -> Integer
primePiStr n = unsafePerformIO $ do
withCString (show n) $ \nString -> do
let len = 32 :: CSize
allocaArray (fromIntegral len) $ \(res :: CString) -> do
ret <- primecount_pi_str nString res len
if ret < 0 || ret > fromIntegral len
then error "primePi: call to primecount_pi_str failed"
else do
answer <- peekCString res
maybe
(error "primePi: couldn't parse result of primecount_pi_str")
pure
(readMaybe answer)
{-# NOINLINE primePiStr #-}
-- | The nth prime, starting at @nthPrime 1 == 2@.
--
-- * Throws an error if the input is less than 1.
-- * Throws an error if the input is larger than 'nthPrimeMaxBound`.
nthPrime :: Integral a => a -> a
nthPrime n
| n < 1 = error "nthPrime: n must be >= 1"
| n' > bound = error "nthPrime: answer cannot be packed into a 64-bit int"
| otherwise = fromIntegral (primecount_nth_prime (fromInteger n'))
where
bound :: Integer
bound = 216289611853439384
n' :: Integer
n' = toInteger n
-- | The largest input supported by 'nthPrime', equal to
-- @primePi ('maxBound' :: 'Int64') == 216289611853439384@.
nthPrimeMaxBound :: Integer
nthPrimeMaxBound = 216289611853439384
-- | @primePhi n a@ counts the number of positive integers @<= n@ that are not
-- divisible by any of the first @a@ primes. Throws an error if @n@ is larger
-- than @'maxBound' :: 'Int64'@.
primePhi :: Integral a => a -> a -> a
primePhi n a
| n <= 0 = 0
| a <= 0 = n
| n' > bound = error "primePhi: input cannot be packed into a 64-bit int"
| otherwise = fromIntegral (primecount_phi (fromInteger n') (fromInteger a'))
where
bound :: Integer
bound = toInteger (maxBound :: Int64)
n' :: Integer
n' = toInteger n
a' :: Integer
a' = min bound (toInteger a)
-- | Get the currently set number of threads used by @libprimecount@.
getNumPrimecountThreads :: IO Int
getNumPrimecountThreads = primecount_get_num_threads
-- | Set the number of threads used by @libprimecount@. If the input is not
-- positive, the thread count is set to 1. If the input is greater than the
-- number of CPUs available, the thread count is set to the number of CPUs
-- available.
setNumPrimecountThreads :: Int -> IO ()
setNumPrimecountThreads = primecount_set_num_threads
-- | Get the @libprimecount@ version number, in the form @"i.j"@
primecountVersion :: String
primecountVersion = unsafePerformIO (peekCString primecount_version)
{-# NOINLINE primecountVersion #-}