diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 0000000..9cee8a5 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,15 @@ +# Changelog + +## 0.1.0.0 + +### Added +* `Math.NumberTheory.Prime.Count.FFI`, with FFI bindings to all functions from the + `primecount` library. +* `Math.NumberTheory.Prime.Count`, with high-level wrappers (`primePi`, + `nthPrime`, and `primePhi`) around the functions in + `Math.NumberTheory.Prime.Count.FFI`. +* Test coverage of `primePi`, `nthPrime`, and `primePhi` with `tasty-hunit`. +* Benchmarks of `primePi`, `nthPrime`, and `primePhi` with `tasty-bench`. +* Full Haddock documentation coverage. +* Support for GHC 9.2.1, 9.0.1, 8.10.7, 8.8.4, 8.6.5, and 8.4.4, and + libprimecount v7.x, verified by GitHub Actions. diff --git a/README.md b/README.md index e531d0c..9ee4912 100644 --- a/README.md +++ b/README.md @@ -6,31 +6,33 @@ This library provides Haskell bindings to Kim Walisch's ## Build instructions First follow the -[installation directions](https://github.com/kimwalisch/primecount#installation) -for the original `primecount` library. As stated in the original directions, if -you are installing through your system's package manager, make sure to get the -development package, which might have a name like `primecount-devel`. +[directions](https://github.com/kimwalisch/primecount#installation) +for installing `libprimecount`. As stated in the directions, if you are +installing through your system's package manager, make sure to get the +development version of the primecount package, which might have a name like +`primecount-devel`. The current version of the Haskell bindings supports any +version of `libprimecount >= 7.0`. Then you can build the Haskell bindings with Stack or Cabal, and read the documentation. ``` -# stack +# Stack stack build stack test stack haddock primecount --open -# cabal +# Cabal cabal update cabal build cabal test cabal haddock # and then open the documentation manually ``` -### Building `libprimecount.so` from source +### Building `libprimecount` from source If you build and install the original `primecount` library from source, instead of through a package manager, then you need to make sure that your Haskell build -system knows where to find it. For example, on Linux it might be installed to -`/usr/local/lib64`. +system knows where to find it. For example, on Linux `libprimecount` might be +installed to `/usr/local/lib64`. Then if using Stack, add the following lines to your `stack.yaml` or your global `~/.stack/config.yaml`: @@ -53,4 +55,4 @@ as an argument to Cabal. ## Bugs Report any bugs on the Github issue tracker, or by emailing -primecount-haskell@mail.preetham.io +[primecount-haskell@mail.preetham.io](mailto:primecount-haskell@mail.preetham.io). diff --git a/bench/Main.hs b/bench/Main.hs index c27c05b..85be6b7 100644 --- a/bench/Main.hs +++ b/bench/Main.hs @@ -1,5 +1,8 @@ -{-# OPTIONS_GHC -Wno-all #-} - +-- | +-- Copyright : 2022 Preetham Gujjula +-- License : BSD-3-Clause +-- Maintainer : primecount-haskell@mail.preetham.io +-- Stability : experimental module Main (main) where import Math.NumberTheory.Prime.Count diff --git a/package.yaml b/package.yaml index c466cef..efd32df 100644 --- a/package.yaml +++ b/package.yaml @@ -6,13 +6,14 @@ license: BSD-3-Clause author: "Preetham Gujjula" maintainer: "primecount-haskell@mail.preetham.io" copyright: "2021 Preetham Gujjula" -category: Math +category: Math, Number Theory description: Please see the README on Github at tested-with: GHC ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.1 || ==9.2.1 extra-source-files: - README.md +- ChangeLog.md ghc-options: - -Wall @@ -26,6 +27,9 @@ ghc-options: - -Wredundant-constraints - -Wmissing-export-lists +default-extensions: +- ScopedTypeVariables + dependencies: - base >= 4.7 && < 5.0 @@ -43,22 +47,22 @@ library: tests: primecount-tests: source-dirs: test + main: Main.hs dependencies: - primecount - silently - tasty - tasty-hunit - main: Main.hs other-modules: - Test.Math.NumberTheory.Prime.Count benchmarks: primecount-bench: source-dirs: bench + main: Main.hs dependencies: - primecount - tasty-bench - main: Main.hs when: - condition: false other-modules: Paths_primecount diff --git a/primecount.cabal b/primecount.cabal index 8ba843b..c6101b0 100644 --- a/primecount.cabal +++ b/primecount.cabal @@ -8,7 +8,7 @@ name: primecount version: 0.1.0.0 synopsis: Bindings to the primecount library description: Please see the README on Github at -category: Math +category: Math, Number Theory homepage: https://github.com/pgujjula/primecount-haskell#readme bug-reports: https://github.com/pgujjula/primecount-haskell/issues author: Preetham Gujjula @@ -21,6 +21,7 @@ tested-with: GHC ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.1 || ==9.2.1 extra-source-files: README.md + ChangeLog.md source-repository head type: git @@ -32,6 +33,8 @@ library Math.NumberTheory.Prime.Count.FFI hs-source-dirs: src + default-extensions: + ScopedTypeVariables ghc-options: -Wall -Wcompat -Wmissing-export-lists -Wincomplete-record-updates -Wpartial-fields -Wmissing-home-modules -Werror=missing-home-modules -Widentities -Wredundant-constraints -Wmissing-export-lists extra-libraries: primecount @@ -46,6 +49,8 @@ test-suite primecount-tests Test.Math.NumberTheory.Prime.Count hs-source-dirs: test + default-extensions: + ScopedTypeVariables ghc-options: -Wall -Wcompat -Wmissing-export-lists -Wincomplete-record-updates -Wpartial-fields -Wmissing-home-modules -Werror=missing-home-modules -Widentities -Wredundant-constraints -Wmissing-export-lists build-depends: base >=4.7 && <5.0 @@ -60,6 +65,8 @@ benchmark primecount-bench main-is: Main.hs hs-source-dirs: bench + default-extensions: + ScopedTypeVariables ghc-options: -Wall -Wcompat -Wmissing-export-lists -Wincomplete-record-updates -Wpartial-fields -Wmissing-home-modules -Werror=missing-home-modules -Widentities -Wredundant-constraints -Wmissing-export-lists build-depends: base >=4.7 && <5.0 diff --git a/src/Math/NumberTheory/Prime/Count.hs b/src/Math/NumberTheory/Prime/Count.hs index e67978b..9e05cc2 100644 --- a/src/Math/NumberTheory/Prime/Count.hs +++ b/src/Math/NumberTheory/Prime/Count.hs @@ -1,9 +1,7 @@ -{-# LANGUAGE ScopedTypeVariables #-} - -- | -- Module : Math.NumberTheory.Prime.Count -- Copyright : 2021 Preetham Gujjula --- License : BSD3 +-- License : BSD-Clause -- Maintainer : primecount-haskell@mail.preetham.io -- Stability : experimental -- @@ -14,10 +12,11 @@ module Math.NumberTheory.Prime.Count ( primePi, primePiMaxBound, nthPrime, + nthPrimeMaxBound, primePhi, getNumPrimecountThreads, setNumPrimecountThreads, - primecountVersion + primecountVersion, ) where @@ -36,8 +35,9 @@ import Text.Read (readMaybe) primePi :: Integral a => a -> a primePi n | n < 0 = 0 - | n' > bound = fromInteger (primePiStr n') - | otherwise = fromIntegral (primecount_pi (fromInteger n')) + | 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) @@ -45,7 +45,7 @@ primePi n n' :: Integer n' = toInteger n --- | The largest input supported by @primePi@. +-- | The largest input supported by 'primePi'. -- -- * 64-bit CPUs: @10^31@ -- * 32-bit CPUs: @2^63 - 1@ @@ -67,12 +67,12 @@ primePiStr n = unsafePerformIO $ do (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 --- @primePi ('maxBound' :: 'Int64') == 216289611853439384@. +-- * 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" @@ -85,6 +85,11 @@ nthPrime n 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'@. @@ -104,18 +109,18 @@ primePhi n a a' :: Integer a' = min bound (toInteger a) --- | Get the currently set number of threads used by libprimecount. +-- | 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 +-- | 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"@ +-- | Get the @libprimecount@ version number, in the form @"i.j"@ primecountVersion :: String primecountVersion = unsafePerformIO (peekCString primecount_version) {-# NOINLINE primecountVersion #-} diff --git a/src/Math/NumberTheory/Prime/Count/FFI.hs b/src/Math/NumberTheory/Prime/Count/FFI.hs index 0dd4dfc..c77562e 100644 --- a/src/Math/NumberTheory/Prime/Count/FFI.hs +++ b/src/Math/NumberTheory/Prime/Count/FFI.hs @@ -1,7 +1,7 @@ -- | -- Module : Math.NumberTheory.Prime.Count.FFI -- Copyright : 2021 Preetham Gujjula --- License : BSD3 +-- License : BSD-3-Clause -- Maintainer : primecount-haskell@mail.preetham.io -- Stability : experimental -- @@ -21,7 +21,7 @@ module Math.NumberTheory.Prime.Count.FFI primecount_get_max_x, primecount_get_num_threads, primecount_set_num_threads, - primecount_version + primecount_version, ) where @@ -33,7 +33,7 @@ import Foreign.C.Types (CSize (..)) foreign import ccall unsafe "primecount_pi" primecount_pi :: Int64 -> Int64 --- | Count the number of primes <= x (supports 128-bit) +-- | Count the number of primes <= x (supports 128-bit). foreign import ccall unsafe "primecount_pi_str" primecount_pi_str :: CString -> CString -> CSize -> IO Int @@ -51,20 +51,17 @@ foreign import ccall unsafe "primecount_phi" -- -- * 64-bit CPUs: @10^31@ -- * 32-bit CPUs: @2^63 - 1@ --- --- The return type is a 'CString' as @primecount_get_max_x@ may be a --- 128-bit integer which is not supported by some compilers. foreign import ccall unsafe "primecount_get_max_x" primecount_get_max_x :: CString --- | Get the currently set number of threads +-- | Get the currently set number of threads used by @libprimecount@. foreign import ccall unsafe "primecount_get_num_threads" primecount_get_num_threads :: IO Int --- | Set the number of threads +-- | Set the number of threads used by @libprimecount@. foreign import ccall unsafe "primecount_set_num_threads" primecount_set_num_threads :: Int -> IO () --- | Get the primecount version number, in the form “i.j” +-- | Get the @libprimecount@ version number, in the form @"i.j"@. foreign import ccall unsafe "primecount_version" primecount_version :: CString diff --git a/stack.yaml b/stack.yaml index a7a59d8..39b4858 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: lts-18.19 +resolver: lts-18.21 packages: - '.' diff --git a/test/Main.hs b/test/Main.hs index 026d00a..cffa55d 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -1,6 +1,6 @@ -- | -- Copyright : 2021 Preetham Gujjula --- License : BSD3 +-- License : BSD-3-Clause -- Maintainer : primecount-haskell@mail.preetham.io -- Stability : experimental module Main (main) where diff --git a/test/Test/Math/NumberTheory/Prime/Count.hs b/test/Test/Math/NumberTheory/Prime/Count.hs index be5bde4..d41b051 100644 --- a/test/Test/Math/NumberTheory/Prime/Count.hs +++ b/test/Test/Math/NumberTheory/Prime/Count.hs @@ -1,15 +1,19 @@ -{-# LANGUAGE ScopedTypeVariables #-} - -- | -- Copyright : 2021 Preetham Gujjula --- License : BSD3 +-- License : BSD-3-Clause -- Maintainer : primecount-haskell@mail.preetham.io -- Stability : experimental module Test.Math.NumberTheory.Prime.Count (tests) where import Control.Exception (SomeException, catch, evaluate) import Data.Int (Int64) -import Math.NumberTheory.Prime.Count (nthPrime, primePhi, primePi) +import Math.NumberTheory.Prime.Count + ( nthPrime, + nthPrimeMaxBound, + primePhi, + primePi, + primePiMaxBound, + ) import System.IO (stderr) import System.IO.Silently (hSilence) import Test.Tasty (TestTree, testGroup) @@ -36,11 +40,10 @@ primePiTest = primePi (5 :: Int) @?= 3 primePi (10 :: Int) @?= 4 primePi (100 :: Int) @?= 25, - testCase "throws error when input is too large" $ do - let limit :: Integer - limit = 10 ^ (31 :: Int) + 1 - exceptionThrown <- throwsException (primePi limit) - exceptionThrown @?= True + testCase "throws error when input is too large" $ + let tooBig :: Integer + tooBig = primePiMaxBound + 1 + in throwsException (primePi tooBig) >>= (@?= True) ] nthPrimeTest :: TestTree @@ -48,19 +51,16 @@ nthPrimeTest = testGroup "nthPrime" [ testCase "throws error for non-positive inputs" $ do - throwsException (nthPrime (0 :: Int)) - >>= (@?= True) - throwsException (nthPrime ((-1) :: Int)) - >>= (@?= True), + throwsException (nthPrime (0 :: Int)) >>= (@?= True) + throwsException (nthPrime ((-1) :: Int)) >>= (@?= True), testCase "works for small inputs" $ do nthPrime (1 :: Int) @?= 2 nthPrime (5 :: Int) @?= 11 nthPrime (25 :: Int) @?= 97, testCase "throws error when input is too large" $ - let limit :: Int64 - limit = 216289611853439384 - in throwsException (nthPrime (limit + 1)) - >>= (@?= True) + let tooBig :: Integer + tooBig = nthPrimeMaxBound + 1 + in throwsException (nthPrime tooBig) >>= (@?= True) ] primePhiTest :: TestTree @@ -80,13 +80,11 @@ primePhiTest = primePhi (10 :: Int) 4 @?= 1 primePhi (10 :: Int) 5 @?= 1, testCase "throws error if n is too large" $ do - throwsException (primePhi (limit + 1) 2) - >>= (@?= True) - throwsException (primePhi (limit + 1) 3) - >>= (@?= True), + throwsException (primePhi tooBig 2) >>= (@?= True) + throwsException (primePhi tooBig 3) >>= (@?= True), testCase "works even if a is too large" $ - primePhi 10 (limit + 1) @?= 1 + primePhi 10 tooBig @?= 1 ] where - limit :: Integer - limit = toInteger (maxBound :: Int64) + tooBig :: Integer + tooBig = toInteger (maxBound :: Int64) + 1