diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+0.6 :
+
+- add Chinese Restaurant Process
+
 0.5 :
 
 - add Log-normal, Laplace, Weibull distributions
diff --git a/splitmix-distributions.cabal b/splitmix-distributions.cabal
--- a/splitmix-distributions.cabal
+++ b/splitmix-distributions.cabal
@@ -1,5 +1,5 @@
 name:           splitmix-distributions
-version:        0.5.0.0
+version:        0.6.0.0
 description:    Random samplers for some common distributions, as well as a convenient interface for composing them, based on splitmix. Please see the README on GitHub at <https://github.com/ocramz/splitmix-distributions#readme>
 homepage:       https://github.com/ocramz/splitmix-distributions#readme
 bug-reports:    https://github.com/ocramz/splitmix-distributions/issues
@@ -11,7 +11,7 @@
 license:        BSD3
 license-file:   LICENSE
 build-type:     Simple
-cabal-version:  1.12
+cabal-version:  >= 1.10
 tested-with:    GHC == 8.10.4
 extra-source-files:
     README.md
@@ -27,11 +27,12 @@
   hs-source-dirs:
       src
   build-depends:
-      base >=4.7 && <5
-    , erf
-    , mtl
-    , splitmix
-    , transformers
+                    base >=4.7 && <5
+                  , containers >= 0.6.2.1
+                  , erf
+                  , mtl
+                  , splitmix
+                  , transformers
   default-language: Haskell2010
 
 test-suite test
diff --git a/src/System/Random/SplitMix/Distributions.hs b/src/System/Random/SplitMix/Distributions.hs
--- a/src/System/Random/SplitMix/Distributions.hs
+++ b/src/System/Random/SplitMix/Distributions.hs
@@ -51,6 +51,7 @@
   categorical,
   discrete,
   zipf,
+  crp,
   -- * PRNG
   -- ** Pure
   Gen, sample, samples,
@@ -64,8 +65,11 @@
 import Data.Foldable (toList)
 import Data.Functor.Identity (Identity(..))
 import Data.List (findIndex)
+import Data.Monoid (Sum(..))
 import GHC.Word (Word64)
 
+-- containers
+import qualified Data.IntMap as IM
 -- erf
 import Data.Number.Erf (InvErf(..))
 -- mtl
@@ -195,6 +199,53 @@
   let (ps, xs) = unzip (toList d)
   midx <- categorical ps
   pure $ (xs !!) <$> midx
+
+
+-- | Chinese restaurant process
+crp :: Monad m =>
+       Double -- ^ concentration parameter \( \alpha \gt 1 \)
+    -> Int -- ^ number of customers \( n > 0 \)
+    -> GenT m [Integer]
+crp a n = do
+    ts <- go crpInitial 1
+    pure $ toList (fmap getSum ts)
+  where
+    go acc i
+      | i == n = pure acc
+      | otherwise = do
+          acc' <- crpSingle i acc a
+          go acc' (i + 1)
+{-# INLINABLE crp #-}
+
+-- | Update step of the CRP
+crpSingle :: (Monad m, Integral a) =>
+             Int -> CRPTables (Sum a) -> Double -> GenT m (CRPTables (Sum a))
+crpSingle i zs a = do
+    znm1 <- categorical probs
+    case znm1 of
+      Just zn1 -> pure $ crpInsert zn1 zs
+      _ -> pure mempty
+  where
+    probs = pms <> [pm1]
+    acc m = fromIntegral m / (fromIntegral i - 1 + a)
+    pms = toList $ fmap (acc . getSum) zs
+    pm1 = a / (fromIntegral i - 1 + a)
+
+-- Tables at the Chinese Restaurant
+newtype CRPTables c = CRP {
+    getCRPTables :: IM.IntMap c
+  } deriving (Eq, Show, Functor, Foldable, Semigroup, Monoid)
+
+-- Initial state of the CRP : one customer sitting at table #0
+crpInitial :: CRPTables (Sum Integer)
+crpInitial = crpInsert 0 mempty
+
+-- Seat one customer at table 'k'
+crpInsert :: Num a => IM.Key -> CRPTables (Sum a) -> CRPTables (Sum a)
+crpInsert k (CRP ts) = CRP $ IM.insertWith (<>) k (Sum 1) ts
+
+
+
 
 -- | Uniform between two values
 uniformR :: Monad m =>
