module Math.Combinat.Numbers where
import Data.Array
paritySign :: Integral a => a -> Integer
paritySign k = if odd k then (1) else 1
factorial :: Integral a => a -> Integer
factorial n
| n < 0 = error "factorial: input should be nonnegative"
| n == 0 = 1
| otherwise = product [1..fromIntegral n]
doubleFactorial :: Integral a => a -> Integer
doubleFactorial n
| n < 0 = error "doubleFactorial: input should be nonnegative"
| n == 0 = 1
| odd n = product [1,3..fromIntegral n]
| otherwise = product [2,4..fromIntegral n]
binomial :: Integral a => a -> a -> Integer
binomial n k
| k > n = 0
| k < 0 = 0
| k > (n `div` 2) = binomial n (nk)
| otherwise = (product [n'k'+1 .. n']) `div` (product [1..k'])
where
k' = fromIntegral k
n' = fromIntegral n
multinomial :: Integral a => [a] -> Integer
multinomial xs = div
(factorial (sum xs))
(product [ factorial x | x<-xs ])
catalan :: Integral a => a -> Integer
catalan n
| n < 0 = 0
| otherwise = binomial (n+n) n `div` fromIntegral (n+1)
catalanTriangle :: Integral a => a -> a -> Integer
catalanTriangle n k
| k > n = 0
| k < 0 = 0
| otherwise = binomial (n+k) n * fromIntegral (nk+1) `div` fromIntegral (n+1)
signedStirling1stArray :: Integral a => a -> Array Int Integer
signedStirling1stArray n
| n < 1 = error "stirling1stArray: n should be at least 1"
| n == 1 = listArray (1,1 ) [1]
| otherwise = listArray (1,n') [ lkp (k1) fromIntegral (n1) * lkp k | k<-[1..n'] ]
where
prev = signedStirling1stArray (n1)
n' = fromIntegral n :: Int
lkp j | j < 1 = 0
| j >= n' = 0
| otherwise = prev ! j
signedStirling1st :: Integral a => a -> a -> Integer
signedStirling1st n k
| k < 1 = 0
| k > n = 0
| otherwise = signedStirling1stArray n ! (fromIntegral k)
unsignedStirling1st :: Integral a => a -> a -> Integer
unsignedStirling1st n k = abs (signedStirling1st n k)
stirling2nd :: Integral a => a -> a -> Integer
stirling2nd n k
| k < 1 = 0
| k > n = 0
| otherwise = sum xs `div` factorial k where
xs = [ paritySign (ki) * binomial k i * (fromIntegral i)^n | i<-[0..k] ]
bernoulli :: Integral a => a -> Rational
bernoulli n
| n < 0 = error "bernoulli: n should be nonnegative"
| n == 0 = 1
| n == 1 = 1/2
| otherwise = sum [ f k | k<-[1..n] ]
where
f k = toRational (paritySign (n+k) * factorial k * stirling2nd n k)
/ toRational (k+1)
coinSeries :: [Int] -> [Integer]
coinSeries [] = 1 : repeat 0
coinSeries (k:ks) = xs where
xs = zipWith (+) (coinSeries ks) (replicate k 0 ++ xs)