summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/math/rational_funcs.tcl
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 19:39:39 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 19:39:39 (GMT)
commitea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch)
tree6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/modules/math/rational_funcs.tcl
parentb5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff)
parent7c621da28f07e449ad90c387344f07a453927569 (diff)
downloadblt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip
blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz
blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/modules/math/rational_funcs.tcl')
-rwxr-xr-xtcllib/modules/math/rational_funcs.tcl364
1 files changed, 364 insertions, 0 deletions
diff --git a/tcllib/modules/math/rational_funcs.tcl b/tcllib/modules/math/rational_funcs.tcl
new file mode 100755
index 0000000..2dae397
--- /dev/null
+++ b/tcllib/modules/math/rational_funcs.tcl
@@ -0,0 +1,364 @@
+# rational_funcs.tcl --
+# Implement procedures to deal with rational functions
+#
+
+package require math::polynomials
+
+namespace eval ::math::rationalfunctions {
+ variable count 0 ;# Count the number of specific commands
+ namespace eval v {}
+
+ namespace export rationalFunction ratioCmd evalRatio \
+ coeffsNumerator coeffsDenominator \
+ derivRatio \
+ addRatio subRatio multRatio \
+ divRatio
+
+ namespace import ::math::polynomials::*
+}
+
+
+# rationalFunction --
+# Return a rational function definition
+#
+# Arguments:
+# num The coefficients of the numerator
+# den The coefficients of the denominator
+# Result:
+# Rational function definition
+#
+proc ::math::rationalfunctions::rationalFunction {num den} {
+
+ foreach coeffs [list $num $den] {
+ foreach coeff $coeffs {
+ if { ! [string is double -strict $coeff] } {
+ return -code error "Coefficients must be real numbers"
+ }
+ }
+ }
+
+ #
+ # The leading coefficient must be non-zero
+ #
+ return [list RATIONAL_FUNCTION [polynomial $num] [polynomial $den]]
+}
+
+# ratioCmd --
+# Return a procedure that implements a rational function evaluation
+#
+# Arguments:
+# num The coefficients of the numerator
+# den The coefficients of the denominator
+# Result:
+# New procedure
+#
+proc ::math::rationalfunctions::ratioCmd {num {den {}}} {
+ variable count
+
+ if { [llength $den] == 0 } {
+ if { [lindex $num 0] == "RATIONAL_FUNCTION" } {
+ set den [lindex $num 2]
+ set num [lindex $num 1]
+ }
+ }
+
+ set degree1 [expr {[llength $num]-1}]
+ set degree2 [expr {[llength $num]-1}]
+ set body "expr \{([join $num +\$x*(][string repeat ) $degree1])/\
+(double([join $den +\$x*(][string repeat ) $degree2])\}"
+
+ incr count
+ set name "::math::rationalfunctions::v::RATIO$count"
+ proc $name {x} $body
+ return $name
+}
+
+# evalRatio --
+# Evaluate a rational function at a given coordinate
+#
+# Arguments:
+# ratio Rational function definition
+# x Coordinate
+# Result:
+# Value at x
+#
+proc ::math::rationalfunctions::evalRatio {ratio x} {
+ if { [lindex $ratio 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Not a rational function"
+ }
+ if { ! [string is double $x] } {
+ return -code error "Coordinate must be a real number"
+ }
+
+ set num 0.0
+ foreach c [lindex [lindex $ratio 1] 1] {
+ set num [expr {$num*$x+$c}]
+ }
+
+ set den 0.0
+ foreach c [lindex [lindex $ratio 2] 1] {
+ set den [expr {$den*$x+$c}]
+ }
+ return [expr {$num/double($den)}]
+}
+
+# coeffsNumerator --
+# Return the coefficients of the numerator
+#
+# Arguments:
+# ratio Rational function definition
+# Result:
+# The coefficients in ascending order
+#
+proc ::math::rationalfunctions::coeffsNumerator {ratio} {
+ if { [lindex $ratio 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Not a rational function"
+ }
+ set polyn [lindex $ratio 1]
+ return [allCoeffsPolyn $polyn]
+}
+
+# coeffsDenominator --
+# Return the coefficients of the denominator
+#
+# Arguments:
+# ratio Rational function definition
+# Result:
+# The coefficients in ascending order
+#
+proc ::math::rationalfunctions::coeffsDenominator {ratio} {
+ if { [lindex $ratio 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Not a rational function"
+ }
+ set polyn [lindex $ratio 2]
+ return [allCoeffsPolyn $polyn]
+}
+
+# derivRatio --
+# Return the derivative of the rational function
+#
+# Arguments:
+# polyn Polynomial definition
+# Result:
+# The new polynomial
+#
+proc ::math::rationalfunctions::derivRatio {ratio} {
+ if { [lindex $ratio 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Not a rational function"
+ }
+ set num_polyn [lindex $ratio 1]
+ set den_polyn [lindex $ratio 2]
+ set num_deriv [derivPolyn $num_polyn]
+ set den_deriv [derivPolyn $den_polyn]
+ set num [subPolyn [multPolyn $num_deriv $den_polyn] \
+ [multPolyn $den_deriv $num_polyn] ]
+ set den [multPolyn $den_polyn $den_polyn]
+
+ return [list RATIONAL_FUNCTION $num $den]
+}
+
+# addRatio --
+# Add two rational functions and return the result
+#
+# Arguments:
+# ratio1 First rational function or a scalar
+# ratio2 Second rational function or a scalar
+# Result:
+# The sum of the two functions
+# Note:
+# TODO: Check for the same denominator
+#
+proc ::math::rationalfunctions::addRatio {ratio1 ratio2} {
+ if { [llength $ratio1] == 1 && [string is double -strict $ratio1] } {
+ set polyn1 [rationalFunction $ratio1 1.0]
+ }
+ if { [llength $ratio2] == 1 && [string is double -strict $ratio2] } {
+ set ratio2 [rationalFunction $ratio1 1.0]
+ }
+ if { [lindex $ratio1 0] != "RATIONAL_FUNCTION" ||
+ [lindex $ratio2 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Both arguments must be rational functions or a real number"
+ }
+
+ set num1 [lindex $ratio1 1]
+ set den1 [lindex $ratio1 2]
+ set num2 [lindex $ratio2 1]
+ set den2 [lindex $ratio2 2]
+
+ set newnum [addPolyn [multPolyn $num1 $den2] \
+ [multPolyn $num2 $den1] ]
+
+ set newden [multPolyn $den1 $den2]
+
+ return [list RATIONAL_FUNCTION $newnum $newden]
+}
+
+# subRatio --
+# Subtract two rational functions and return the result
+#
+# Arguments:
+# ratio1 First rational function or a scalar
+# ratio2 Second rational function or a scalar
+# Result:
+# The difference of the two functions
+# Note:
+# TODO: Check for the same denominator
+#
+proc ::math::rationalfunctions::subRatio {ratio1 ratio2} {
+ if { [llength $ratio1] == 1 && [string is double -strict $ratio1] } {
+ set polyn1 [rationalFunction $ratio1 1.0]
+ }
+ if { [llength $ratio2] == 1 && [string is double -strict $ratio2] } {
+ set ratio2 [rationalFunction $ratio1 1.0]
+ }
+ if { [lindex $ratio1 0] != "RATIONAL_FUNCTION" ||
+ [lindex $ratio2 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Both arguments must be rational functions or a real number"
+ }
+
+ set num1 [lindex $ratio1 1]
+ set den1 [lindex $ratio1 2]
+ set num2 [lindex $ratio2 1]
+ set den2 [lindex $ratio2 2]
+
+ set newnum [subPolyn [multPolyn $num1 $den2] \
+ [multPolyn $num2 $den1] ]
+
+ set newden [multPolyn $den1 $den2]
+
+ return [list RATIONAL_FUNCTION $newnum $newden]
+}
+
+# multRatio --
+# Multiply two rational functions and return the result
+#
+# Arguments:
+# ratio1 First rational function or a scalar
+# ratio2 Second rational function or a scalar
+# Result:
+# The product of the two functions
+# Note:
+# TODO: Check for the same denominator
+#
+proc ::math::rationalfunctions::multRatio {ratio1 ratio2} {
+ if { [llength $ratio1] == 1 && [string is double -strict $ratio1] } {
+ set polyn1 [rationalFunction $ratio1 1.0]
+ }
+ if { [llength $ratio2] == 1 && [string is double -strict $ratio2] } {
+ set ratio2 [rationalFunction $ratio1 1.0]
+ }
+ if { [lindex $ratio1 0] != "RATIONAL_FUNCTION" ||
+ [lindex $ratio2 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Both arguments must be rational functions or a real number"
+ }
+
+ set num1 [lindex $ratio1 1]
+ set den1 [lindex $ratio1 2]
+ set num2 [lindex $ratio2 1]
+ set den2 [lindex $ratio2 2]
+
+ set newnum [multPolyn $num1 $num2]
+ set newden [multPolyn $den1 $den2]
+
+ return [list RATIONAL_FUNCTION $newnum $newden]
+}
+
+# divRatio --
+# Divide two rational functions and return the result
+#
+# Arguments:
+# ratio1 First rational function or a scalar
+# ratio2 Second rational function or a scalar
+# Result:
+# The quotient of the two functions
+# Note:
+# TODO: Check for the same denominator
+#
+proc ::math::rationalfunctions::divRatio {ratio1 ratio2} {
+ if { [llength $ratio1] == 1 && [string is double -strict $ratio1] } {
+ set polyn1 [rationalFunction $ratio1 1.0]
+ }
+ if { [llength $ratio2] == 1 && [string is double -strict $ratio2] } {
+ set ratio2 [rationalFunction $ratio1 1.0]
+ }
+ if { [lindex $ratio1 0] != "RATIONAL_FUNCTION" ||
+ [lindex $ratio2 0] != "RATIONAL_FUNCTION" } {
+ return -code error "Both arguments must be rational functions or a real number"
+ }
+
+ set num1 [lindex $ratio1 1]
+ set den1 [lindex $ratio1 2]
+ set num2 [lindex $ratio2 1]
+ set den2 [lindex $ratio2 2]
+
+ set newnum [multPolyn $num1 $den2]
+ set newden [multPolyn $num2 $den1]
+
+ return [list RATIONAL_FUNCTION $newnum $newden]
+}
+
+#
+# Announce our presence
+#
+package provide math::rationalfunctions 1.0.1
+
+# some tests --
+#
+if { 0 } {
+set prec $::tcl_precision
+if {![package vsatisfies [package provide Tcl] 8.5]} {
+ set ::tcl_precision 17
+} else {
+ set ::tcl_precision 0
+}
+
+
+set f1 [::math::rationalfunctions::rationalFunction {1 2 3} {1 4}]
+set f2 [::math::rationalfunctions::rationalFunction {1 2 3 0} {1 4}]
+set f3 [::math::rationalfunctions::rationalFunction {0 0 0 0} {1}]
+set f4 [::math::rationalfunctions::rationalFunction {5 7} {1}]
+set cmdf1 [::math::rationalfunctions::ratioCmd {1 2 3} {1 4}]
+
+foreach x {0 1 2 3 4 5} {
+ puts "[::math::rationalfunctions::evalRatio $f1 $x] -- \
+[expr {(1.0+2.0*$x+3.0*$x*$x)/double(1.0+4.0*$x)}] -- \
+[$cmdf1 $x] -- [::math::rationalfunctions::evalRatio $f3 $x]"
+}
+
+puts "All coefficients = [::math::rationalfunctions::coeffsNumerator $f2]"
+puts " [::math::rationalfunctions::coeffsDenominator $f2]"
+
+puts "Derivative = [::math::rationalfunctions::derivRatio $f1]"
+
+puts "Add: [::math::rationalfunctions::addRatio $f1 $f4]"
+puts "Add: [::math::rationalfunctions::addRatio $f4 $f1]"
+puts "Subtract: [::math::rationalfunctions::subRatio $f1 $f4]"
+puts "Multiply: [::math::rationalfunctions::multRatio $f1 $f4]"
+
+set f1 [::math::rationalfunctions::rationalFunction {1 2 3} 1]
+set f2 [::math::rationalfunctions::rationalFunction {0 1} 1]
+
+puts "Divide: [::math::rationalfunctions::divRatio $f1 $f2]"
+
+set f1 [::math::rationalfunctions::rationalFunction {1 2 3} 1]
+set f2 [::math::rationalfunctions::rationalFunction {1 1} {1 2}]
+
+puts "Divide: [::math::rationalfunctions::divRatio $f1 $f2]"
+
+set f1 [::math::rationalfunctions::rationalFunction {1 2 3} 1]
+set f2 [::math::rationalfunctions::rationalFunction {0 1} {0 0 1}]
+set f3 [::math::rationalfunctions::divRatio $f2 $f1]
+set coeffs [::math::rationalfunctions::coeffsNumerator $f3]
+puts "Coefficients: $coeffs"
+set f3 [::math::rationalfunctions::divRatio $f1 $f2]
+set coeffs [::math::rationalfunctions::coeffsNumerator $f3]
+puts "Coefficients: $coeffs"
+set f1 [::math::rationalfunctions::rationalFunction {1 2 3} {1 2}]
+set f2 [::math::rationalfunctions::rationalFunction {0} {1}]
+set f3 [::math::rationalfunctions::divRatio $f2 $f1]
+set coeffs [::math::rationalfunctions::coeffsNumerator $f3]
+puts "Coefficients: $coeffs"
+puts "Eval null function: [::math::rationalfunctions::evalRatio $f2 1]"
+
+set ::tcl_precision $prec
+}