)abbrev package COMBINAT IntegerCombinatoricFunctions
++ Authors: Martin Brock, Robert Sutor, Michael Monagan
++ Date Created: June 1987
++ Basic Operations:
++ Related Domains:
++ Also See:
++ AMS Classifications:
++ Keywords: integer, combinatoric function
++ Examples:
++ References:
++ Description:
++   The \spadtype{IntegerCombinatoricFunctions} package provides some
++   standard functions in combinatorics.
Z   ==> Integer
N   ==> NonNegativeInteger
SUP ==> SparseUnivariatePolynomial

IntegerCombinatoricFunctions(I : IntegerNumberSystem) : with
   binomial : (I, I) -> I
      ++ \spad{binomial(n, r)} returns the binomial coefficient
      ++ \spad{C(n, r) = n!/(r! (n-r)!)}, where \spad{n >= r >= 0}.
      ++ This is the number of combinations of n objects taken r at a time.
   factorial : I -> I
      ++ \spad{factorial(n)} returns \spad{n!}. this is the product of all
      ++ integers between 1 and n (inclusive).
      ++ Note: \spad{0!} is defined to be 1.
   multinomial : (I, List I) -> I
      ++ \spad{multinomial(n, [m1, m2, ..., mk])} returns the multinomial
      ++ coefficient \spad{n!/(m1! m2! ... mk!)}.
   partition : I -> I
      ++ \spad{partition(n)} returns the number of partitions of the integer n.
      ++ This is the number of distinct ways that n can be written as
      ++ a sum of positive integers.
   distinct_partition : I -> I
      ++ \spad{distinct_partition(n)} returns the number of partitions
      ++ of the integer n with distinct members.  This is the number of
      ++ ways that n can be written as a sum of distinct positive
      ++ integers.  For \spad{n > 0} this is the same as number of
      ++ ways that n can be written as a sum of odd positive
      ++ integers.
   permutation : (I, I) -> I
      ++ \spad{permutation(n)} returns \spad{!P(n, r) = n!/(n-r)!}. This is
      ++ the number of permutations of n objects taken r at a time.
   stirling1 : (I, I) -> I
      ++ \spad{stirling1(n, m)} returns the Stirling number of the first kind
      ++ denoted \spad{S[n, m]}.
   stirling2 : (I, I) -> I
      ++ \spad{stirling2(n, m)} returns the Stirling number of the second kind
      ++ denoted \spad{SS[n, m]}.
   catalan : I -> I
      ++ \spad{catalan(n)} returns the n-th Catalan number
 == add
   F : Record(Fn : I, Fv : I) := [0, 1]
   B : Record(Bn : I, Bm : I, Bv : I) := [0, 0, 0]
   S : Record(Sn : I, Sp : SUP I) := [0, 0]
   P : IndexedFlexibleArray(I, 0) := new(1, 1)$IndexedFlexibleArray(I, 0)

   partition n ==
      -- This is the number of ways of expressing n as a sum of positive
      -- integers, without regard to order.  For example partition 5 = 7
      -- since 5 = 1+1+1+1+1 = 1+1+1+2 = 1+2+2 = 1+1+3 = 1+4 = 2+3 = 5 .
      -- Uses O(sqrt n) term recurrence from Abramowitz & Stegun pp. 825
      -- p(n) = sum (-1)^(k + 1) p(n - j) where
      -- 0 < j := (3*k^2 +- k) quo 2 <= n
      minIndex(P) ~= 0 => error "Partition: must have minIndex of 0"
      m := #P
      n < 0 => error "partition is not defined for negative integers"
      n < m::I => P(convert(n)@Z)
      concat!(P, new((convert(n+1)@Z - m)::N, 0)$IndexedFlexibleArray(I, 0))
      for i in m..convert(n)@Z repeat
         s : I := 1
         t : I := 0
         for k in 1.. repeat
            l := (3*k*k-k) quo 2
            l > i => break
            u := l+k
            t := t + s * P(convert(i-l)@Z)
            u > i => break
            t := t + s * P(convert(i-u)@Z)
            s := -s
         P.i := t
      P(convert(n)@Z)

   distinct_partition(n : I) : I ==
       -- Uses O(sqrt n) term recurrence from Abramowitz & Stegun pp. 826
       -- q(n) = u + sum (-1)^(k + 1) q(n - j) where
       -- 0 < j := (3*k^2 +- k) quo 2 <= n, u = (-1)^r if n = 3*r^2 +- k
       -- and u = 0 otherwise
       nz := convert(n)@Z
       nz < 0 =>
           error "distinct_partition is not defined for negative integers"
       Q := new((nz + 1)::N, 0)$IndexedOneDimensionalArray(I, 0)
       Q(0) := Q(1) := 1
       for i in 2..nz repeat
           s : I := 1
           t : I := 0
           for k in 1$Z.. repeat
               l1 := (3*k*k - k)
               if l1 = i or l1 + 2*k = i then
                   t := t - s
               l := l1 quo 2
               l > i => break
               u := l + k
               t := t + s*Q(i - l)
               u > i => break
               t := t + s*Q(i - u)
               s := -s
           Q(i) := t
       Q(nz)

   factorial n ==
      s, f, t : I
      n < 0 => error "factorial not defined for negative integers"
      if n <= F.Fn then s := f := 1 else (s, f) := F
      for k in convert(s+1)@Z .. convert(n)@Z by 2 repeat
         if k::I = n then t := n else t := k::I * (k+1)::I
         f := t * f
      F.Fn := n
      F.Fv := f

   binomial(n, m) ==
      s, b : I
      n < 0 or m < 0 or m > n => 0
      m = 0 => 1
      n < 2*m => binomial(n, n-m)
      (s, b) := (0, 1)
      if B.Bn = n then
         B.Bm = m+1 =>
            b := (B.Bv * (m+1)) quo (n-m)
            B.Bn := n
            B.Bm := m
            return(B.Bv := b)
         if m >= B.Bm then (s := B.Bm; b := B.Bv) else (s, b) := (0, 1)
      for k in convert(s+1)@Z .. convert(m)@Z repeat
        b := (b*(n-k::I+1)) quo k::I
      B.Bn := n
      B.Bm := m
      B.Bv := b

   multinomial(n, m) ==
      for t in m repeat t < 0 => return 0
      n < _+/m => 0
      s : I := 1
      for t in m repeat s := s * factorial t
      factorial n quo s

   permutation(n, m) ==
      t : I
      m < 0 or n < m => 0
      m := n-m
      p : I := 1
      for k in convert(m+1)@Z .. convert(n)@Z by 2 repeat
         if k::I = n then t := n else t := (k*(k+1))::I
         p := p * t
      p

   stirling1(n, m) ==
      -- Definition: (-1)^(n-m) S[n, m] is the number of
      -- permutations of n symbols which have m cycles.
      n < 0 or m < 1 or m > n => 0
      m = n => 1
      S.Sn = n => coefficient(S.Sp, convert(m)@Z :: N)
      x := monomial(1, 1)$SUP(I)
      S.Sn := n
      S.Sp := x
      for k in 1 .. convert(n-1)@Z repeat S.Sp := S.Sp * (x - k::SUP(I))
      coefficient(S.Sp, convert(m)@Z :: N)

   stirling2(n, m) ==
      -- definition: SS[n, m] is the number of ways of partitioning
      -- a set of n elements into m non-empty subsets
      n < 0 or m < 1 or m > n => 0
      m = 1 or n = m => 1
      s : I := if odd? m then -1 else 1
      t : I := 0
      for k in 1..convert(m)@Z repeat
         s := -s
         t := t + s * binomial(m, k::I) * k::I ^ (convert(n)@Z :: N)
      t quo factorial m

   catalan(n) == (binomial(2*n, n) exquo (n+1))::I

--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
