content: import Control.Monad (guard)
  import Data.List (intercalate)
  import Data.List.Split (chunksOf)
  import Math.NumberTheory.Primes (Prime, unPrime, nextPrime)
  import Math.NumberTheory.Primes.Testing (isPrime)
  import Text.Printf (printf)
  
  data PierPointKind = First | Second
  
  merge :: Ord a => [a] -> [a] -> [a]
  merge [] b = b
  merge a@(x:xs) b@(y:ys) | x < y     = x : merge xs b
                          | otherwise = y : merge a ys
  
  nSmooth :: Integer -> [Integer]
  nSmooth p = 1 : foldr u [] factors
   where
    factors = takeWhile (<=p) primes
    primes = map unPrime [nextPrime 1..]
    u n s = r
     where
      r = merge s (map (n*) (1:r))
  
  pierpoints :: PierPointKind -> [Integer]
  pierpoints k = do
    n <- nSmooth 3
    let x = case k of First  -> succ n
                      Second -> pred n
    guard (isPrime x) >> [x]
  
  main :: IO ()
  main = do
    printf "\nFirst 50 Pierpont primes of the first kind:\n"
    mapM_ (\row -> mapM_ (printf "%12s" . commas) row >> printf "\n") (rows $ pierpoints First)
    printf "\nFirst 50 Pierpont primes of the second kind:\n"
    mapM_ (\row -> mapM_ (printf "%12s" . commas) row >> printf "\n") (rows $ pierpoints Second)
    printf "\n250th Pierpont prime of the first kind: %s\n" (commas $ pierpoints First !! 249)
    printf "\n250th Pierpont prime of the second kind: %s\n\n" (commas $ pierpoints Second !! 249)
   where
    rows = chunksOf 10 . take 50
    commas = reverse . intercalate "," . chunksOf 3 . reverse . show
  