{-# LANGUAGE FlexibleInstances #-}

-- | Adds a few useful operators/functions to |Num|.

module Data.Stream.Hinze.NumExt (
    module Data.Stream.Hinze.NumExt,
    module Ratio
 ) where

import Prelude (Eq(..), Ord(..), Num(..), Integral(..), Integer, error, otherwise)
import qualified Prelude
import Ratio

infixl 7 /
infixr 8 ^

class (Num a, Ord a) => NumExt a where
   (/), (^)     :: a -> a -> a  -- NB. we include '/' to be able to define 'choose' uniformly
   fact         :: a -> a
   fall, choose :: a -> a -> a

   -- | Factorials.
   fact 0 =  1
   fact n =  n * fact (n - 1)

   -- | Falling factorial powers (see CMath, p.47).
   fall _ 0  =  1
   fall x n  =  x * fall (x - 1) (n - 1)

   -- | Binomial coefficients.

   choose x k
        | k < 0      =  0
        | otherwise  =  fall x k / fact k  -- TODO: improve

instance NumExt Integer where
  (^) = (Prelude.^)
  (/) = div

instance (NumExt a, Integral a) => NumExt (Ratio a) where
   m ^ n = if denominator n == 1
           then (numerator m ^ numerator n) % (denominator m ^ numerator n)
           else error "^: Ratio"
   (/) = (Prelude./)