If you were on the math team during high school, you may remember integer partitions not too fondly. They're not particularly easy to get a grip on: even counting the partitions of an integer requires generating functions (which are scary when you're in high school).

But they're easily tractable with a computer, and so here we present functional algorithms for integer partitions, written in Haskell. It does use recursion-schemes, but this is more than justified by the added eloquence.

import Numeric.Natural       (Natural)
import Control.Monad         (join)
import Data.List             (nub)
import Data.Functor.Foldable (ListF (..), para)


partitions :: Natural -> [[Natural]]
partitions = para algebra
    where algebra Nothing          = []
          algebra (Just (0,\_))     = [[1]]
          algebra (Just (\_, past)) = (nub . (getAll =<<)) (fmap (1:) past)

getAll :: [Natural] -> [[Natural]]
getAll = fmap (dropWhile (==0) . sort) . subsets
    where subsets xs = flip sumIndicesAt xs <$> indices xs

indices :: [Natural] -> [[Natural]]
indices = join . para algebra
    where algebra Nil                 = []
          algebra (Cons x (xs, []))   = [[x:xs]]
          algebra (Cons x (xs, past)) = (:) <$> [x:xs,[]] <*> past

sumIndicesAt :: [Natural] -> [Natural] -> [Natural]
sumIndicesAt ix = (\\(a, b) -> sum a : b) . partition (\`elem\` ix)

While this is beautiful on its own (not to mention an instructive use of paramorphisms), we also get a very satisfying application of QuickCheck using Ramanujan's congruences, viz.

$$p(5k + 4) \equiv 0 (\text{mod } 5)$$ $$p(7k + 5) \equiv 0 (\text{mod } 7)$$ $$p(11 k + 6) \equiv 0 (\text{mod } 11)$$

where \(p(n)\) is the number of ways to partition some integer \(n\).

This leads to the following property test using hspec:

import Test.Hspec
import Test.QuickCheck
import Numeric.Naturals

main :: IO ()
main = hspec $
    describe "partitions" $
        it "should satisfy Ramanujan's identities (1/3)" $
            property $ \n -> (length . partitions) (5 \* n  + 4) `mod` 5 == (0 :: Natural)
        it "should satisfy Ramanujan's identities (2/3)" $
            property $ \n -> (length . partitions) (7 \* n  + 5) `mod` 7 == (0 :: Natural)
        it "should satisfy Ramanujan's identities (3/3)" $
            property $ \n -> (length . partitions) (11 \* n  + 6) `mod` 11 == (0 :: Natural)

From which we can confirm that our function does what we desire with a cosmic level of confidence.