racket/collects/math/private/number-theory/factorial.rkt
Neil Toronto 60dd8d065f Fixes for errors Pierpaolo Bernardi found by reviewing the docs; also,
renamed `partition-count' to `partitions' to be consistent with
`permutations', and gave better examples in `multinomial' docs

 * (flulp-error +inf.0 +nan.0) was returning +nan.0 instead of +inf.0

 * Type of `multinomial' didn't match its docs or `flmultinomial'

 * Reworded docs for `diagonal-array'

 * Reworked/reordered quite a few things in docs for `math/bigfloat'

 * Fixed first identity given in `gamma-inc' docs

 * Fixed descrption for `+max.0', etc.
2012-12-17 11:40:37 -07:00

68 lines
2.6 KiB
Racket

#lang typed/racket/base
(require "../unsafe.rkt")
(provide factorial permutations multinomial)
(define-predicate nonnegative-fixnum? Nonnegative-Fixnum)
;; The number of factorials whose flonum representation is finite
;; Makes generating the flonum table in gamma.rkt fast
(: fact-table-size Positive-Fixnum)
(define fact-table-size 171)
(: fact-table (Vectorof Positive-Integer))
(define fact-table
(list->vector
(reverse
(foldl (λ: ([n : Positive-Integer]
[ns : (Listof Positive-Integer)])
(cons (* n (car ns)) ns))
'(1)
(build-list (- fact-table-size 1) add1)))))
(: simple-cutoff Positive-Fixnum)
;; The point at which it seems to be faster to use a more complicated recurrence
(define simple-cutoff 244)
(: factorial-simple (Nonnegative-Fixnum -> Positive-Integer))
(define (factorial-simple n)
(cond [(n . < . fact-table-size) (vector-ref fact-table n)]
[else (* n (factorial-simple (- n 1)))]))
(: factorial (case-> (Zero -> One)
(One -> One)
(Integer -> Positive-Integer)))
(define (factorial n)
(cond [(negative? n) (raise-argument-error 'factorial "Natural" n)]
[(not (fixnum? n)) (raise-argument-error 'factorial "Nonnegative-Fixnum" n)]
[(eqv? n 0) 1]
[(eqv? n 1) 1]
[(n . < . simple-cutoff) (factorial-simple n)]
[else
(let: loop : Positive-Integer ([n : Positive-Fixnum n]
[m : Positive-Fixnum 1])
(define n-m (- n m))
(cond [(n-m . <= . 0) n]
[else (define 2m (unsafe-fx* m 2))
(* (loop n 2m) (loop n-m 2m))]))]))
(: permutations (case-> (Integer Zero -> One)
(One One -> One)
(Integer Integer -> Natural)))
(define (permutations n k)
(cond [(negative? n) (raise-argument-error 'permutations "Natural" 0 n k)]
[(negative? k) (raise-argument-error 'permutations "Natural" 1 n k)]
[(zero? k) 1]
[(k . > . n) 0]
[else (assert (/ (factorial n) (factorial (- n k)))
exact-nonnegative-integer?)]))
(: multinomial (Integer (Listof Integer) -> Natural))
(define (multinomial n ks)
(cond [(negative? n) (raise-argument-error 'multinomial "Natural" 0 n ks)]
[(ormap negative? ks) (raise-argument-error 'multinomial "(Listof Natural)" 1 n ks)]
[(not (= n (apply + ks))) 0]
[else (assert (apply / (factorial n) (map factorial ks))
exact-nonnegative-integer?)]))