exact-combinatorics-0.2.1.0: src/Math/Combinatorics/Exact/Primes.hs
{-# OPTIONS_GHC
-Wall
-fwarn-tabs
-fno-warn-name-shadowing
-fno-warn-incomplete-patterns
-fno-warn-incomplete-uni-patterns
#-}
----------------------------------------------------------------
-- 2026-09-28
-- |
-- Module : Math.Combinatorics.Exact.Primes
-- Copyright : Copyright (c) 2011--2026 wren gayle romano
-- License : BSD
-- Maintainer : wren@cpan.org
-- Stability : experimental
-- Portability : Haskell98
--
-- The prime numbers (<http://oeis.org/A000040>).
----------------------------------------------------------------
module Math.Combinatorics.Exact.Primes (primes) where
-- TODO: With the exception of the lists stored in a 'Wheel', all
-- the lists in this file are in fact infinite (modulo size issues
-- about 'Int'). Therefore it would be nice to implement (or find
-- on hackage) a datatype for infinite lists to avoid the cost of
-- unnecessary branches in case analysis, and to avoid the need for
-- `-fno-warn-incomplete-patterns` and `-fno-warn-incomplete-uni-patterns`.
-- In particular, we need the monad\/list-comprehension and @(++)@;
-- which alas seems to indicate that we need to use finite-lists
-- intermediately to constructing the infinite lists, unless we can
-- be especially clever.
data Wheel = Wheel {-# UNPACK #-}!Int ![Int]
-- BUG: the CAF is nice for sharing, but what about when we want
-- fusion and to avoid sharing? Using "Data.IntList" seems to only
-- increase the overhead. I guess things aren't being memoized/freed
-- like they should...
-- | The prime numbers. Implemented with the algorithm in:
--
-- * Colin Runciman (1997)
-- /Lazy Wheel Sieves and Spirals of Primes/, Functional Pearl,
-- Journal of Functional Programming, 7(2). pp.219--225.
-- ISSN 0956-7968
-- <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.7096>
-- TODO: get a new url for the paper, since citeseer is dead.
--
primes :: [Int]
primes = seive wheels primes primeSquares
where
primeSquares :: [Int]
primeSquares = [p*p | p <- primes]
wheels :: [Wheel]
wheels = Wheel 1 [1] : zipWith nextSize wheels primes
where
nextSize :: Wheel -> Int -> Wheel
nextSize (Wheel s ns) p =
Wheel (s*p) [n' | o <- [0,s..(p-1)*s]
, n <- ns
, let n' = n+o
, n' `mod` p > 0 ]
-- NOTE: I've switched to using lazy-patterns in lieu of 'head'
-- and 'tail' in order to silence warnings on GHC >= 9.10.
-- However, beware the syntax problems of combining as-patterns
-- with lazy-patterns on GHC >= 9.0:
-- <https://stackoverflow.com/q/67972231>
-- <https://gitlab.haskell.org/ghc/ghc/-/wikis/migration/9.0#whitespace-sensitive----and->
--
-- Also note that `-fno-warn-incomplete-patterns` is no longer
-- sufficient to silence the errors about incompete patterns here;
-- we additionally need `-fno-warn-incomplete-uni-patterns`.
-- Moreover, this isn't something we can resolve by simply expanding
-- out the impossible cases, for some strange reason.
seive :: [Wheel] -> [Int] -> [Int] -> [Int]
-- NOTE: @pps@ and @qqs@ must be lazy; or else the circular program is _|_.
seive (Wheel s ns : ws) pps@(~(p:ps)) qqs@(~(_:qs)) =
[ n' | o <- s : [2*s,3*s..(p-1)*s]
, n <- ns
, let n' = n+o
, s <= 2 || noFactorIn pps qqs n' ]
++ seive ws ps qs
where
noFactorIn :: [Int] -> [Int] -> Int -> Bool
noFactorIn (p:ps) (q:qs) x =
q > x || x `mod` p > 0 && noFactorIn ps qs x
----------------------------------------------------------------
----------------------------------------------------------- fin.