content: import           Data.List.Split          (chunksOf)
  import qualified Data.MemoCombinators  as Memo
  import           Math.NumberTheory.Primes (unPrime, factorise)
  import           Text.Printf              (printf)
  
  moebius :: Integer -> Int
  moebius = product . fmap m . factorise
    where
      m (p, e)
        | unPrime p == 0 = 0
        | e == 1 = -1
        | otherwise = 0
  
  mertens :: Integer -> Int
  mertens = Memo.integral (\n -> sum $ fmap moebius [1..n])
  
  countZeros :: [Integer] -> Int
  countZeros = length . filter ((==0) . mertens)
  
  crossesZero :: [Integer] -> Int
  crossesZero = length . go . fmap mertens
    where
      go (x:y:xs)
        | y == 0 && x /= 0 = y : go (y:xs)
        | otherwise        = go (y:xs)
      go _ = []
  
  main :: IO ()
  main = do
    printf "The first 99 terms for M(1..99):\n\n   "
    mapM_ (printf "%3d" . mertens) [1..9] >> printf "\n"
    mapM_ (\row -> mapM_ (printf "%3d" . mertens) row >> printf "\n") $ chunksOf 10 [10..99]
    printf "\nM(n) is zero %d times for 1 <= n <= 1000.\n" $ countZeros [1..1000]
    printf "M(n) crosses zero %d times for 1 <= n <= 1000.\n" $ crossesZero [1..1000]
  