Attachment "combinatorics.patch" to
ticket [484850ffff]
added by
kennykb
2001-12-15 04:49:57.
Index: modules/math/combinatorics.tcl
===================================================================
RCS file: combinatorics.tcl
diff -N combinatorics.tcl
--- /dev/null Thu May 24 22:33:05 2001
+++ combinatorics.tcl Fri Dec 14 13:08:23 2001
@@ -0,0 +1,436 @@
+#----------------------------------------------------------------------
+#
+# math/combinatorics.tcl --
+#
+# This file contains definitions of mathematical functions
+# useful in combinatorial problems.
+#
+# Copyright (c) 2001, by Kevin B. Kenny. All rights reserved.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: $
+#
+#----------------------------------------------------------------------
+
+package require Tcl 8.0
+
+namespace eval ::math {
+
+ # Commonly used combinatorial functions
+
+ namespace export ln_Gamma; # Logarithm of the Gamma function
+ namespace export factorial; # Factorial
+ namespace export choose; # Binomial coefficient
+ namespace export Beta; # Beta function
+
+}
+
+#----------------------------------------------------------------------
+#
+# ::math::initializeFactorial --
+#
+# Initialize a table of factorials for small integer arguments.
+#
+# Parameters:
+# None.
+#
+# Results:
+# None.
+#
+# Side effects:
+# The variable, ::math::factorialList, is initialized to hold
+# a table of factorial n for 0 <= n <= 170.
+#
+# This procedure is called once when the 'factorial' procedure is
+# being loaded.
+#
+#----------------------------------------------------------------------
+
+proc ::math::initializeFactorial {} {
+
+ variable [namespace current]::factorialList
+
+ set factorialList [list 1]
+ set f 1
+ for { set i 1 } { $i < 171 } { incr i } {
+ if { $i > 12. } {
+ set f [expr { $f * double($i)}]
+ } else {
+ set f [expr { $f * $i }]
+ }
+ lappend factorialList $f
+ }
+
+}
+
+#----------------------------------------------------------------------
+#
+# ::math::initializePascal --
+#
+# Precompute the first few rows of Pascal's triangle and store
+# them in the variable ::math::pascal
+#
+# Parameters:
+# None.
+#
+# Results:
+# None.
+#
+# Side effects:
+# ::math::pascal is initialized to a flat list containing
+# the first 34 rows of Pascal's triangle. C(n,k) is to be found
+# at [lindex $pascal $i] where i = n * ( n + 1 ) + k. No attempt
+# is made to exploit symmetry.
+#
+#----------------------------------------------------------------------
+
+proc ::math::initializePascal {} {
+
+ variable pascal
+
+ set pascal [list 1]
+ for { set n 1 } { $n < 34 } { incr n } {
+ lappend pascal 1
+ set l2 [list 1]
+ for { set k 1 } { $k < $n } { incr k } {
+ set km1 [expr { $k - 1 }]
+ set c [expr { [lindex $l $km1] + [lindex $l $k] }]
+ lappend pascal $c
+ lappend l2 $c
+ }
+ lappend pascal 1
+ lappend l2 1
+ set l $l2
+ }
+
+}
+
+#----------------------------------------------------------------------
+#
+# ::math::ln_Gamma --
+#
+# Returns ln(Gamma(x)), where x >= 0
+#
+# Parameters:
+# x - Argument to the Gamma function.
+#
+# Results:
+# Returns the natural logarithm of Gamma(x).
+#
+# Side effects:
+# None.
+#
+# Gamma(x) is defined as:
+#
+# +inf
+# _
+# | x-1 -t
+# Gamma(x)= _| t e dt
+#
+# 0
+#
+# The approximation used here is from Lanczos, SIAM J. Numerical Analysis,
+# series B, volume 1, p. 86. For x > 1, the absolute error of the
+# result is claimed to be smaller than 5.5 * 10**-10 -- that is, the
+# resulting value of Gamma when exp( ln_Gamma( x ) ) is computed is
+# expected to be precise to better than nine significant figures.
+#
+#----------------------------------------------------------------------
+
+proc ::math::ln_Gamma { x } {
+
+ # Handle the common case of a real argument that's within the
+ # permissible range.
+
+ if { [string is double $x]
+ && ( $x > 0 )
+ && ( $x <= 2.5563481638716906e+305 )
+ } {
+ set x [expr { $x - 1.0 }]
+ set tmp [expr { $x + 5.5 }]
+ set tmp [ expr { ( $x + 0.5 ) * log( $tmp ) - $tmp }]
+ set ser 1.0
+ foreach cof {
+ 76.18009173 -86.50532033 24.01409822
+ -1.231739516 .00120858003 -5.36382e-6
+ } {
+ set x [expr { $x + 1.0 }]
+ set ser [expr { $ser + $cof / $x }]
+ }
+ return [expr { $tmp + log( 2.50662827465 * $ser ) }]
+ }
+
+ # Handle the error cases.
+
+ if { ![string is double $x] } {
+ return -code error [expectDouble $x]
+ }
+
+ if { $x <= 0.0 } {
+ set proc [lindex [info level 0] 0]
+ return -code error \
+ -errorcode [list ARITH DOMAIN \
+ "argument to $proc must be positive"] \
+ "argument to $proc must be positive"
+ }
+
+ return -code error \
+ -errorcode [list ARITH RANGE \
+ "floating-point value too large to represent"] \
+ "floating-point value too large to represent"
+
+}
+
+#----------------------------------------------------------------------
+#
+# math::factorial --
+#
+# Returns the factorial of the argument x.
+#
+# Parameters:
+# x -- Number whose factorial is to be computed.
+#
+# Results:
+# Returns x!, the factorial of x.
+#
+# Side effects:
+# None.
+#
+# For integer x, 0 <= x <= 12, an exact integer result is returned.
+#
+# For integer x, 13 <= x <= 21, an exact floating-point result is returned
+# on machines with IEEE floating point.
+#
+# For integer x, 22 <= x <= 170, the result is exact to 1 ULP.
+#
+# For real x, x >= 0, the result is approxumated by computing
+# Gamma(x+1) using the ::math::ln_Gamma function, and the result is
+# expected to be precise to better than nine significant figures.
+#
+# It is an error to present x <= -1 or x > 170, or a value of x that
+# is not numeric.
+#
+#----------------------------------------------------------------------
+
+proc ::math::factorial { x } {
+
+ variable [namespace current]::factorialList
+
+ # Common case: factorial of a small integer
+
+ if { [string is integer $x]
+ && $x >= 0
+ && $x <= [llength $factorialList] } {
+ return [lindex $factorialList $x]
+ }
+
+ # Error case: not a number
+
+ if { ![string is double $x] } {
+ return -code error [expectDouble $x]
+ }
+
+ # Error case: gamma in the left half plane
+
+ if { $x <= -1.0 } {
+ set proc [lindex [info level 0] 0]
+ set message "argument to $proc must be greater than -1.0"
+ return -code error -errorcode [list ARITH DOMAIN $message] $message
+ }
+
+ # Error case - gamma fails
+
+ if { [catch { expr exp( [ln_Gamma [expr { $x + 1 }]] ) } result] } {
+ return -code error -errorcode $::errorCode $result
+ }
+
+ # Success - computed factorial n as Gamma(n+1)
+
+ return $result
+
+}
+
+#----------------------------------------------------------------------
+#
+# ::math::choose --
+#
+# Returns the binomial coefficient C(n,k) = n!/k!(n-k)!
+#
+# Parameters:
+# n -- Number of objects in the sampling pool
+# k -- Number of objects to be chosen.
+#
+# Results:
+# Returns C(n,k).
+#
+# Side effects:
+# None.
+#
+# Results are expected to be accurate to ten significant figures.
+# If both parameters are integers and the result fits in 32 bits,
+# the result is rounded to an integer.
+#
+# Integer results are exact up to at least n = 34.
+# Floating point results are precise to better than nine significant
+# figures.
+#
+#----------------------------------------------------------------------
+
+proc ::math::choose { n k } {
+
+ variable pascal
+
+ # Use a precomputed table for small integer args
+
+ if { [string is integer $n]
+ && $n >= 0 && $n < 34
+ && [string is integer $k]
+ && $k >= 0 && $k <= $n } {
+
+ set i [expr { ( ( $n * ($n + 1) ) / 2 ) + $k }]
+
+ return [lindex $pascal $i]
+
+ }
+
+ # Test bogus arguments
+
+ if { ![string is double $n] } {
+ return -code error [expectDouble $n]
+ }
+ if { ![string is double $k] } {
+ return -code error [expectDouble $k]
+ }
+
+ # Forbid negative n
+
+ if { $n < 0. } {
+ set proc [lindex [info level 0] 0]
+ set msg "first argument to $proc must be non-negative"
+ return -code error -errorcode [list ARITH DOMAIN $msg] $msg
+ }
+
+ # Handle k out of range
+
+ if { [string is integer $k] && [string is integer $n]
+ && ( $k < 0 || $k > $n ) } {
+ return 0
+ }
+
+ if { $k < 0. } {
+ set proc [lindex [info level 0] 0]
+ set msg "second argument to $proc must be non-negative,\
+ or both must be integers"
+ return -code error -errorcode [list ARITH DOMAIN $msg] $msg
+ }
+
+ # Compute the logarithm of the desired binomial coefficient.
+
+ if { [catch { expr { [ln_Gamma [expr { $n + 1 }]]
+ - [ln_Gamma [expr { $k + 1 }]]
+ - [ln_Gamma [expr { $n - $k + 1 }]] } } r] } {
+ return -code error -errorcode $::errorCode $r
+ }
+
+ # Compute the binomial coefficient itself
+
+ if { [catch { expr { exp( $r ) } } r] } {
+ return -code error -errorcode $::errorCode $r
+ }
+
+ # Round to integer if both args are integers and the result fits
+
+ if { $r <= 2147483647.5
+ && [string is integer $n]
+ && [string is integer $k] } {
+ return [expr { round( $r ) }]
+ }
+
+ return $r
+
+}
+
+#----------------------------------------------------------------------
+#
+# ::math::Beta --
+#
+# Return the value of the Beta function of paramters z and w.
+#
+# Parameters:
+# z, w : Two real parameters to the Beta function
+#
+# Results:
+# Returns the value of the Beta function.
+#
+# Side effects:
+# None.
+#
+# Beta( w, z ) is defined as:
+#
+# 1_
+# | (z-1) (w-1)
+# Beta( w, z ) = Beta( z, w ) = | t (1-t) dt
+# _|
+# 0
+#
+# = Gamma( z ) Gamma( w ) / Gamma( z + w )
+#
+# Results are returned as a floating point number precise to better
+# than nine significant figures for w, z > 1.
+#
+#----------------------------------------------------------------------
+
+proc ::math::Beta { z w } {
+
+ # Check form of both args so that domain check can be made
+
+ if { ![string is double $z] } {
+ return -code error [expectDouble $z]
+ }
+ if { ![string is double $w] } {
+ return -code error [expectDouble $w]
+ }
+
+ # Check sign of both args
+
+ if { $z <= 0.0 } {
+ set proc [lindex [info level 0] 0]
+ set msg "first argument to $proc must be positive"
+ return -code error -errorcode [list ARITH DOMAIN $msg] $msg
+ }
+ if { $w <= 0.0 } {
+ set proc [lindex [info level 0] 0]
+ set msg "second argument to $proc must be positive"
+ return -code error -errorcode [list ARITH DOMAIN $msg] $msg
+ }
+
+ # Compute beta using gamma function, keeping stack trace clean.
+
+ if { [catch { expr { exp( [ln_Gamma $z] + [ln_Gamma $w]
+ - [ln_Gamma [ expr { $z + $w }]] ) } } beta] } {
+
+ return -code error -errorcode $::errorCode $beta
+
+ }
+
+ return $beta
+
+}
+
+#----------------------------------------------------------------------
+#
+# Initialization of this file:
+#
+# Initialize the precomputed tables of factorials and binomial
+# coefficients.
+#
+#----------------------------------------------------------------------
+
+namespace eval ::math {
+ initializeFactorial
+ initializePascal
+}
+
+package provide math 1.1
Index: modules/math/combinatorics.test
===================================================================
RCS file: combinatorics.test
diff -N combinatorics.test
--- /dev/null Thu May 24 22:33:05 2001
+++ combinatorics.test Fri Dec 14 13:08:23 2001
@@ -0,0 +1,294 @@
+# Tests for combinatorics functions in math library -*- tcl -*-
+#
+# This file contains a collection of tests for one or more of the Tcllib
+# procedures. Sourcing this file into Tcl runs the tests and
+# generates output for errors. No output means no errors were found.
+#
+# Copyright (c) 2001 by Kevin B. Kenny
+# All rights reserved.
+#
+# RCS: @(#) $Id: math.test,v 1.7 2000/10/06 21:10:41 ericm Exp $
+
+if {[lsearch [namespace children] ::tcltest] == -1} {
+ package require tcltest
+ namespace import ::tcltest::*
+}
+
+source [file join [file dirname [info script]] math.tcl]
+source [file join [file dirname [info script]] combinatorics.tcl]
+
+package require math
+
+# Fake [lset] for Tcl releases that don't have it. We need only
+# lset into a flat list.
+
+if { [string compare lset [info commands lset]] } {
+ proc K { x y } { set x }
+ proc lset { listVar index var } {
+ upvar 1 $listVar list
+ set list [lreplace [K $list [set list {}]] $index $index $var]
+ }
+}
+
+# Service procedure to develop the error message for "wrong # args"
+
+proc wrongNumArgs {name arglist count} {
+ set ver [info patchlevel]
+ # strip "a1", etc. designations
+ regsub {(a|b)[1-9]$} $ver {} ver
+ if {[package vcompare $ver 8.4] < 0} {
+ set arg [lindex $arglist $count]
+ set msg "no value given for parameter \"$arg\" to \"$name\""
+ } else {
+ set msg "wrong # args: should be \"$name $arglist\""
+ }
+ return $msg
+}
+
+test combinatorics-1.1 { math::ln_Gamma, wrong num args } {
+ catch { math::ln_Gamma } msg
+ set msg
+} [wrongNumArgs math::ln_Gamma x 0]
+
+test combinatorics-1.2 { math::ln_Gamma, main line code } {
+ set maxerror 0.
+ set f 1.
+ for { set i 1 } { $i < 171 } { set i $ip1 } {
+ set f [expr { $f * $i }]
+ set ip1 [expr { $i + 1 }]
+ set f2 [expr { exp( [math::ln_Gamma $ip1] ) }]
+ set error [expr { abs( $f2 - $f ) / $f }]
+ if { $error > $maxerror } {
+ set maxerror $error
+ }
+ }
+ if { $maxerror > 5e-10 } {
+ error "max error of factorials computed using math::ln_Gamma\
+ specified to be 5e-10, was $maxerror"
+ }
+ concat
+} {}
+
+test combinatorics-1.3 { math::ln_Gamma, half integer args } {
+ set maxerror 0.
+ set z 0.5
+ set pi 3.1415926535897932
+ set g [expr { sqrt( $pi ) }]
+ while { $z < 170. } {
+ set g2 [expr { exp( [::math::ln_Gamma $z] ) }]
+ set error [expr { abs( $g2 - $g ) / $g }]
+ if { $error > $maxerror } {
+ set maxerror $error
+ }
+ set g [expr { $g * $z }]
+ set z [expr { $z + 1. }]
+ }
+ if { $maxerror > 5e-10 } {
+ error "max error of half integer gamma computed using math::ln_Gamma\
+ specified to be 5e-10, was $maxerror"
+ }
+ concat
+} {}
+
+test combinatorics-1.4 { math::ln_Gamma, bogus arg } {
+ catch { math::ln_Gamma bogus } msg
+ set msg
+} {expected a floating-point number but found "bogus"}
+
+test combinatorics-1.5 { math::ln_Gamma, evaluate at pole } {
+ catch { math::ln_Gamma 0.0 } msg
+ list $msg $::errorCode
+} {{argument to math::ln_Gamma must be positive} {ARITH DOMAIN {argument to math::ln_Gamma must be positive}}}
+
+test combinatorics-1.6 { math::ln_Gamma, exponent overflow } {
+ catch { math::ln_Gamma 2.556348163871691e+305 } msg
+ list $msg $::errorCode
+} {{floating-point value too large to represent} {ARITH RANGE {floating-point value too large to represent}}}
+
+test combinatorics-2.1 { math::factorial, wrong num args } {
+ catch { math::factorial } msg
+ set msg
+} [wrongNumArgs math::factorial x 0]
+
+test combinatorics-2.2 { math::factorial 0 } {
+ math::factorial 0
+} 1
+
+test combinatorics-2.3 { math::factorial, main line } {
+ set maxerror 0.
+ set f 1.
+ for { set i 1 } { $i < 171 } { set i $ip1 } {
+ set f [expr { $f * $i }]
+ set ip1 [expr { $i + 1 }]
+ set f2 [math::factorial $i]
+ set error [expr { abs( $f2 - $f ) / $f }]
+ if { $error > $maxerror } {
+ set maxerror $error
+ }
+ }
+ if { $maxerror > 1e-16 } {
+ error "max error of factorials computed using math::factorial\
+ specified to be 1e-16, was $maxerror"
+ }
+ concat
+} {}
+
+test combinatorics-2.4 { math::factorial, half integer args } {
+ set maxerror 0.
+ set z -0.5
+ set pi 3.1415926535897932
+ set g [expr { sqrt( $pi ) }]
+ while { $z < 169. } {
+ set g2 [math::factorial $z]
+ set error [expr { abs( $g2 - $g ) / $g }]
+ if { $error > $maxerror } {
+ set maxerror $error
+ }
+ set z [expr { $z + 1. }]
+ set g [expr { $g * $z }]
+ }
+ if { $maxerror > 1e-9 } {
+ error "max error of half integer factorial\
+ specified to be 1e-9, was $maxerror"
+ }
+ concat
+} {}
+
+test combinatorics-2.5 { math::factorial, bogus arg } {
+ catch { math::factorial bogus } msg
+ set msg
+} {expected a floating-point number but found "bogus"}
+
+test combinatorics-2.6 { math::factorial, evaluate at pole } {
+ catch { math::factorial -1.0 } msg
+ list $msg $::errorCode
+} {{argument to math::factorial must be greater than -1.0} {ARITH DOMAIN {argument to math::factorial must be greater than -1.0}}}
+
+test combinatorics-2.7 { math::factorial, exponent overflow } {
+ catch { math::factorial 172 } msg
+ list $msg $::errorCode
+} {{floating-point value too large to represent} {ARITH OVERFLOW {floating-point value too large to represent}}}
+
+test combinatorics-3.1 { math::choose, wrong num args } {
+ catch { math::choose } msg
+ set msg
+} [wrongNumArgs math::choose {n k} 0]
+
+test combinatorics-3.2 { math::choose, wrong num args } {
+ catch { math::choose 1 } msg
+ set msg
+} [wrongNumArgs math::choose {n k} 1]
+
+test combinatorics-3.3 { math::choose, precomputed table and gamma evals } {
+ set maxError 0
+ set l {}
+ for { set n 0 } { $n < 100 } { incr n } {
+ lappend l 1.
+ for { set k [expr { $n - 1 }] } { $k > 0 } { set k $km1 } {
+ set km1 [expr { $k - 1 }]
+ set cnk [expr { [lindex $l $k] + [lindex $l $km1] }]
+ lset l $k $cnk
+ set ccnk [math::choose $n $k]
+ set error [expr { abs( $ccnk - $cnk ) / $cnk }]
+ if { $error > $maxError } {
+ set maxError $error
+ }
+ }
+ }
+ if { $maxError > 5e-10 } {
+ error "max error in math::choose was $maxError, specified to be 5e-10"
+ }
+ concat
+} {}
+
+test combinatorics-3.4 { math::choose, bogus n } {
+ catch { math::choose bogus 0 } msg
+ set msg
+} {expected a floating-point number but found "bogus"}
+
+test combinatorics-3.5 { math::choose bogus k } {
+ catch { math::choose 0 bogus } msg
+ set msg
+} {expected a floating-point number but found "bogus"}
+
+test combinatorics-3.6 { match::choose negative n } {
+ catch { math::choose -1 0 } msg
+ list $msg $::errorCode
+} {{first argument to math::choose must be non-negative} {ARITH DOMAIN {first argument to math::choose must be non-negative}}}
+
+test combinatorics-3.7 { math::choose negative k } {
+ math::choose 17 -1
+} 0
+
+test combinatorics-3.8 { math::choose excess k } {
+ math::choose 17 18
+} 0
+
+test combinatorics-3.9 {math::choose negative fraction } {
+ catch { math::choose 17 -0.5 } msg
+ list $msg $::errorCode
+} {{second argument to math::choose must be non-negative, or both must be integers} {ARITH DOMAIN {second argument to math::choose must be non-negative, or both must be integers}}}
+
+test combinatorics-3.10 { math::choose big args } {
+ catch { math::choose 1500 750 } msg
+ list $msg $::errorCode
+} {{floating-point value too large to represent} {ARITH OVERFLOW {floating-point value too large to represent}}}
+
+test combinatorics-4.1 { math::Beta, wrong num args } {
+ catch { math::Beta } msg
+ set msg
+} [wrongNumArgs math::Beta {z w} 0]
+
+test combinatorics-4.2 { math::Beta, wrong num args } {
+ catch { math::Beta 1 } msg
+ set msg
+} [wrongNumArgs math::Beta {z w} 1]
+
+test combinatorics-4.3 { math::Beta, bogus z } {
+ catch { math::Beta bogus 1 } msg
+ set msg
+} {expected a floating-point number but found "bogus"}
+
+test combinatorics-4.4 { math::Beta, bogus w } {
+ catch { math::Beta 1 bogus } msg
+ set msg
+} {expected a floating-point number but found "bogus"}
+
+test combinatorics-4.5 { math::Beta, negative z } {
+ catch { math::Beta 0 1 } msg
+ list $msg $::errorCode
+} {{first argument to math::Beta must be positive} {ARITH DOMAIN {first argument to math::Beta must be positive}}}
+
+test combinatorics-4.6 { math::Beta, negative w } {
+ catch { math::Beta 1 0 } msg
+ list $msg $::errorCode
+} {{second argument to math::Beta must be positive} {ARITH DOMAIN {second argument to math::Beta must be positive}}}
+
+test combinatorics-4.7 { math::Beta, test with Pascal } {
+ set maxError 0
+ set l {}
+ for { set n 0 } { $n < 100 } { incr n } {
+ lappend l 1.
+ for { set k [expr { $n - 1 }] } { $k > 0 } { set k $km1 } {
+ set km1 [expr { $k - 1 }]
+ set cnk [expr { [lindex $l $k] + [lindex $l $km1] }]
+ lset l $k $cnk
+ set w [expr { $k + 1 }]
+ set z [expr { $n - $k + 1 }]
+ set beta [expr { 1.0 / $cnk / ( $z + $w - 1 )}]
+ set cbeta [math::Beta $z $w]
+ set error [expr { abs( $cbeta - $beta ) / $beta }]
+ if { $error > $maxError } {
+ set maxError $error
+ }
+ }
+ }
+ if { $maxError > 5e-10 } {
+ error "max error in math::Beta was $maxError, specified to be 5e-10"
+ }
+ concat
+} {}
+
+
+::tcltest::cleanupTests
+
Index: modules/math/math.tcl
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/math/math.tcl,v
retrieving revision 1.9
diff -u -r1.9 math.tcl
--- modules/math/math.tcl 2001/08/02 16:38:06 1.9
+++ modules/math/math.tcl 2001/12/14 21:08:23
@@ -335,4 +335,26 @@
set sum
}
+#----------------------------------------------------------------------
+#
+# ::math::expectDouble --
+#
+# Format an error message that an argument was expected to be
+# double and wasn't
+#
+# Parameters:
+# arg -- Misformatted argument
+#
+# Results:
+# Returns an appropriate error message
+#
+# Side effects:
+# None.
+#
+#----------------------------------------------------------------------
+
+proc ::math::expectDouble { arg } {
+ return [format "expected a floating-point number but found \"%.50s\"" $arg]
+}
+
package provide math 1.1
Index: modules/math/pkgIndex.tcl
===================================================================
RCS file: /cvsroot/tcllib/tcllib/modules/math/pkgIndex.tcl,v
retrieving revision 1.4
diff -u -r1.4 pkgIndex.tcl
--- modules/math/pkgIndex.tcl 2001/12/12 18:42:16 1.4
+++ modules/math/pkgIndex.tcl 2001/12/14 21:08:23
@@ -9,6 +9,5 @@
# full path name of this file's directory.
if {![package vsatisfies [package provide Tcl] 8.2]} {return}
-package ifneeded math 1.1 [list source [file join $dir math.tcl]]
+package ifneeded math 1.1 [list source [file join $dir combinatorics.tcl]]\n[list source [file join $dir math.tcl]]
package ifneeded math::geometry 1.0 [list source [file join $dir geometry.tcl]]
-