polynomials.tcl

Go to the documentation of this file.
00001 /*  polynomials.tcl --*/
00002 /*     Implement procedures to deal with polynomial functions*/
00003 /* */
00004 namespace ::math::polynomials {
00005     variable count 0  ;/*  Count the number of specific commands*/
00006     namespace v {}
00007 
00008     namespace export polynomial polynCmd evalPolyn \
00009                      degreePolyn coeffPolyn allCoeffsPolyn \
00010                      derivPolyn  primitivePolyn \
00011                      addPolyn    subPolyn multPolyn \
00012                      divPolyn    remainderPolyn
00013 }
00014 
00015 
00016 /*  polynomial --*/
00017 /*     Return a polynomial definition*/
00018 /* */
00019 /*  Arguments:*/
00020 /*     coeffs       The coefficients of the polynomial*/
00021 /*  Result:*/
00022 /*     Polynomial definition*/
00023 /* */
00024 ret  ::math::polynomials::polynomial (type coeffs) {
00025 
00026     set rev_coeffs {}
00027     set degree     -1
00028     set index       0
00029     foreach coeff $coeffs {
00030         if { ! [string is double -strict $coeff] } {
00031             return -code error "Coefficients must be real numbers"
00032         }
00033         set rev_coeffs [concat $coeff $rev_coeffs]
00034         if { $coeff != 0.0 } {
00035             set degree $index
00036         }
00037         incr index
00038     }
00039 
00040     #
00041     # The leading coefficient must be non-zero
00042     #
00043     return [list POLYNOMIAL [lrange $rev_coeffs end-$degree end]]
00044 }
00045 
00046 /*  polynCmd --*/
00047 /*     Return a procedure that implements a polynomial evaluation*/
00048 /* */
00049 /*  Arguments:*/
00050 /*     coeffs       The coefficients of the polynomial (or a definition)*/
00051 /*  Result:*/
00052 /*     New procedure*/
00053 /* */
00054 ret  ::math::polynomials::polynCmd (type coeffs) {
00055     variable count
00056 
00057     if { [lindex $coeffs 0] == "POLYNOMIAL" } {
00058         set coeffs [allCoeffsPolyn $coeffs]
00059     }
00060 
00061     set degree [expr {[llength $coeffs]-1}]
00062     set body "expr \{[join $coeffs +\$x*(][string repeat ) $degree]\}"
00063 
00064     incr count
00065     set name "::math::polynomials::v::POLYN$count"
00066     proc $name {x} $body
00067     return $name
00068 }
00069 
00070 /*  evalPolyn --*/
00071 /*     Evaluate a polynomial at a given coordinate*/
00072 /* */
00073 /*  Arguments:*/
00074 /*     polyn        Polynomial definition*/
00075 /*     x            Coordinate*/
00076 /*  Result:*/
00077 /*     Value at x*/
00078 /* */
00079 ret  ::math::polynomials::evalPolyn (type polyn , type x) {
00080     if { [lindex $polyn 0] != "POLYNOMIAL" } {
00081         return -code error "Not a polynomial"
00082     }
00083     if { ! [string is double $x] } {
00084         return -code error "Coordinate must be a real number"
00085     }
00086 
00087     set result 0.0
00088     foreach c [lindex $polyn 1] {
00089         set result [expr {$result*$x+$c}]
00090     }
00091     return $result
00092 }
00093 
00094 /*  degreePolyn --*/
00095 /*     Return the degree of the polynomial*/
00096 /* */
00097 /*  Arguments:*/
00098 /*     polyn        Polynomial definition*/
00099 /*  Result:*/
00100 /*     The degree*/
00101 /* */
00102 ret  ::math::polynomials::degreePolyn (type polyn) {
00103     if { [lindex $polyn 0] != "POLYNOMIAL" } {
00104         return -code error "Not a polynomial"
00105     }
00106     return [expr {[llength [lindex $polyn 1]]-1}]
00107 }
00108 
00109 /*  coeffPolyn --*/
00110 /*     Return the coefficient of the index'th degree of the polynomial*/
00111 /* */
00112 /*  Arguments:*/
00113 /*     polyn        Polynomial definition*/
00114 /*     index        Degree for which to return the coefficient*/
00115 /*  Result:*/
00116 /*     The coefficient of degree "index"*/
00117 /* */
00118 ret  ::math::polynomials::coeffPolyn (type polyn , type index) {
00119     if { [lindex $polyn 0] != "POLYNOMIAL" } {
00120         return -code error "Not a polynomial"
00121     }
00122     set coeffs [lindex $polyn 1]
00123     if { $index < 0 || $index > [llength $coeffs] } {
00124         return -code error "Index must be between 0 and [llength $coeffs]"
00125     }
00126     return [lindex $coeffs end-$index]
00127 }
00128 
00129 /*  allCoeffsPolyn --*/
00130 /*     Return the coefficients of the polynomial*/
00131 /* */
00132 /*  Arguments:*/
00133 /*     polyn        Polynomial definition*/
00134 /*  Result:*/
00135 /*     The coefficients in ascending order*/
00136 /* */
00137 ret  ::math::polynomials::allCoeffsPolyn (type polyn) {
00138     if { [lindex $polyn 0] != "POLYNOMIAL" } {
00139         return -code error "Not a polynomial"
00140     }
00141     set rev_coeffs [lindex $polyn 1]
00142     set coeffs {}
00143     foreach c $rev_coeffs {
00144         set coeffs [concat $c $coeffs]
00145     }
00146     return $coeffs
00147 }
00148 
00149 /*  derivPolyn --*/
00150 /*     Return the derivative of the polynomial*/
00151 /* */
00152 /*  Arguments:*/
00153 /*     polyn        Polynomial definition*/
00154 /*  Result:*/
00155 /*     The new polynomial*/
00156 /* */
00157 ret  ::math::polynomials::derivPolyn (type polyn) {
00158     if { [lindex $polyn 0] != "POLYNOMIAL" } {
00159         return -code error "Not a polynomial"
00160     }
00161     set coeffs [lindex $polyn 1]
00162     set new_coeffs {}
00163     set idx        [degreePolyn $polyn]
00164     foreach c [lrange $coeffs 0 end-1] {
00165         lappend new_coeffs [expr {$idx*$c}]
00166         incr idx -1
00167     }
00168     return [list POLYNOMIAL $new_coeffs]
00169 }
00170 
00171 /*  primitivePolyn --*/
00172 /*     Return the primitive of the polynomial*/
00173 /* */
00174 /*  Arguments:*/
00175 /*     polyn        Polynomial definition*/
00176 /*  Result:*/
00177 /*     The new polynomial*/
00178 /* */
00179 ret  ::math::polynomials::primitivePolyn (type polyn) {
00180     if { [lindex $polyn 0] != "POLYNOMIAL" } {
00181         return -code error "Not a polynomial"
00182     }
00183     set coeffs [lindex $polyn 1]
00184     set new_coeffs {}
00185     set idx        [llength $coeffs]
00186     foreach c [lrange $coeffs 0 end] {
00187         lappend new_coeffs [expr {$c/double($idx)}]
00188         incr idx -1
00189     }
00190     return [list POLYNOMIAL [concat $new_coeffs 0.0]]
00191 }
00192 
00193 /*  addPolyn --*/
00194 /*     Add two polynomials and return the result*/
00195 /* */
00196 /*  Arguments:*/
00197 /*     polyn1       First polynomial or a scalar*/
00198 /*     polyn2       Second polynomial or a scalar*/
00199 /*  Result:*/
00200 /*     The sum of the two polynomials*/
00201 /*  Note:*/
00202 /*     Make sure that the first coefficient is not zero*/
00203 /* */
00204 ret  ::math::polynomials::addPolyn (type polyn1 , type polyn2) {
00205     if { [llength $polyn1] == 1 && [string is double -strict $polyn1] } {
00206         set polyn1 [polynomial $polyn1]
00207     }
00208     if { [llength $polyn2] == 1 && [string is double -strict $polyn2] } {
00209         set polyn2 [polynomial $polyn2]
00210     }
00211     if { [lindex $polyn1 0] != "POLYNOMIAL" ||
00212          [lindex $polyn2 0] != "POLYNOMIAL"    } {
00213         return -code error "Both arguments must be polynomials or a real number"
00214     }
00215     set coeffs1 [lindex $polyn1 1]
00216     set coeffs2 [lindex $polyn2 1]
00217 
00218     set extra1  [expr {[llength $coeffs2]-[llength $coeffs1]}]
00219     while { $extra1 > 0 } {
00220         set coeffs1 [concat 0.0 $coeffs1]
00221         incr extra1 -1
00222     }
00223 
00224     set extra2  [expr {[llength $coeffs1]-[llength $coeffs2]}]
00225     while { $extra2 > 0 } {
00226         set coeffs2 [concat 0.0 $coeffs2]
00227         incr extra2 -1
00228     }
00229 
00230     set new_coeffs {}
00231     foreach c1 $coeffs1 c2 $coeffs2 {
00232         lappend new_coeffs [expr {$c1+$c2}]
00233     }
00234     while { [lindex $new_coeffs 0] == 0.0 } {
00235         set new_coeffs [lrange $new_coeffs 1 end]
00236     }
00237     return [list POLYNOMIAL $new_coeffs]
00238 }
00239 
00240 /*  subPolyn --*/
00241 /*     Subtract two polynomials and return the result*/
00242 /* */
00243 /*  Arguments:*/
00244 /*     polyn1       First polynomial or a scalar*/
00245 /*     polyn2       Second polynomial or a scalar*/
00246 /*  Result:*/
00247 /*     The difference of the two polynomials*/
00248 /*  Note:*/
00249 /*     Make sure that the first coefficient is not zero*/
00250 /* */
00251 ret  ::math::polynomials::subPolyn (type polyn1 , type polyn2) {
00252     if { [llength $polyn1] == 1 && [string is double -strict $polyn1] } {
00253         set polyn1 [polynomial $polyn1]
00254     }
00255     if { [llength $polyn2] == 1 && [string is double -strict $polyn2] } {
00256         set polyn2 [polynomial $polyn2]
00257     }
00258     if { [lindex $polyn1 0] != "POLYNOMIAL" ||
00259          [lindex $polyn2 0] != "POLYNOMIAL"    } {
00260         return -code error "Both arguments must be polynomials or a real number"
00261     }
00262     set coeffs1 [lindex $polyn1 1]
00263     set coeffs2 [lindex $polyn2 1]
00264 
00265     set extra1  [expr {[llength $coeffs2]-[llength $coeffs1]}]
00266     while { $extra1 > 0 } {
00267         set coeffs1 [concat 0.0 $coeffs1]
00268         incr extra1 -1
00269     }
00270 
00271     set extra2  [expr {[llength $coeffs1]-[llength $coeffs2]}]
00272     while { $extra2 > 0 } {
00273         set coeffs2 [concat 0.0 $coeffs2]
00274         incr extra2 -1
00275     }
00276 
00277     set new_coeffs {}
00278     foreach c1 $coeffs1 c2 $coeffs2 {
00279         lappend new_coeffs [expr {$c1-$c2}]
00280     }
00281     while { [lindex $new_coeffs 0] == 0.0 } {
00282         set new_coeffs [lrange $new_coeffs 1 end]
00283     }
00284     return [list POLYNOMIAL $new_coeffs]
00285 }
00286 
00287 /*  multPolyn --*/
00288 /*     Multiply two polynomials and return the result*/
00289 /* */
00290 /*  Arguments:*/
00291 /*     polyn1       First polynomial or a scalar*/
00292 /*     polyn2       Second polynomial or a scalar*/
00293 /*  Result:*/
00294 /*     The difference of the two polynomials*/
00295 /*  Note:*/
00296 /*     Make sure that the first coefficient is not zero*/
00297 /* */
00298 ret  ::math::polynomials::multPolyn (type polyn1 , type polyn2) {
00299     if { [llength $polyn1] == 1 && [string is double -strict $polyn1] } {
00300         set polyn1 [polynomial $polyn1]
00301     }
00302     if { [llength $polyn2] == 1 && [string is double -strict $polyn2] } {
00303         set polyn2 [polynomial $polyn2]
00304     }
00305     if { [lindex $polyn1 0] != "POLYNOMIAL" ||
00306          [lindex $polyn2 0] != "POLYNOMIAL"    } {
00307         return -code error "Both arguments must be polynomials or a real number"
00308     }
00309 
00310     set coeffs1 [lindex $polyn1 1]
00311     set coeffs2 [lindex $polyn2 1]
00312 
00313     #
00314     # Take care of the null polynomial
00315     #
00316     if { $coeffs1 == {} || $coeffs2 == {} } {
00317         return [polynomial {}]
00318     }
00319 
00320     set zeros {}
00321     foreach c $coeffs1 {
00322         lappend zeros 0.0
00323     }
00324 
00325     set new_coeffs [lrange $zeros 1 end]
00326     foreach c $coeffs2 {
00327         lappend new_coeffs 0.0
00328     }
00329 
00330     set idx        0
00331     foreach c $coeffs1 {
00332         set term_coeffs {}
00333         foreach c2 $coeffs2 {
00334             lappend term_coeffs [expr {$c*$c2}]
00335         }
00336         set term_coeffs [concat [lrange $zeros 0 [expr {$idx-1}]] \
00337                                 $term_coeffs \
00338                                 [lrange $zeros [expr {$idx+1}] end]]
00339 
00340         set sum_coeffs {}
00341         foreach t $term_coeffs n $new_coeffs {
00342             lappend sum_coeffs [expr {$t+$n}]
00343         }
00344         set new_coeffs $sum_coeffs
00345         incr idx
00346     }
00347 
00348     return [list POLYNOMIAL $new_coeffs]
00349 }
00350 
00351 /*  divPolyn --*/
00352 /*     Divide two polynomials and return the quotient*/
00353 /* */
00354 /*  Arguments:*/
00355 /*     polyn1       First polynomial or a scalar*/
00356 /*     polyn2       Second polynomial or a scalar*/
00357 /*  Result:*/
00358 /*     The difference of the two polynomials*/
00359 /*  Note:*/
00360 /*     Make sure that the first coefficient is not zero*/
00361 /* */
00362 ret  ::math::polynomials::divPolyn (type polyn1 , type polyn2) {
00363     if { [llength $polyn1] == 1 && [string is double -strict $polyn1] } {
00364         set polyn1 [polynomial $polyn1]
00365     }
00366     if { [llength $polyn2] == 1 && [string is double -strict $polyn2] } {
00367         set polyn2 [polynomial $polyn2]
00368     }
00369     if { [lindex $polyn1 0] != "POLYNOMIAL" ||
00370          [lindex $polyn2 0] != "POLYNOMIAL"    } {
00371         return -code error "Both arguments must be polynomials or a real number"
00372     }
00373 
00374     set coeffs1 [lindex $polyn1 1]
00375     set coeffs2 [lindex $polyn2 1]
00376 
00377     #
00378     # Take care of the null polynomial
00379     #
00380     if { $coeffs1 == {} } {
00381         return [polynomial {}]
00382     }
00383     if { $coeffs2 == {} } {
00384         return -code error "Denominator can not be zero"
00385     }
00386 
00387     foreach {quotient remainder} [DivRemPolyn $polyn1 $polyn2] {break}
00388     return $quotient
00389 }
00390 
00391 /*  remainderPolyn --*/
00392 /*     Divide two polynomials and return the remainder*/
00393 /* */
00394 /*  Arguments:*/
00395 /*     polyn1       First polynomial or a scalar*/
00396 /*     polyn2       Second polynomial or a scalar*/
00397 /*  Result:*/
00398 /*     The difference of the two polynomials*/
00399 /*  Note:*/
00400 /*     Make sure that the first coefficient is not zero*/
00401 /* */
00402 ret  ::math::polynomials::remainderPolyn (type polyn1 , type polyn2) {
00403     if { [llength $polyn1] == 1 && [string is double -strict $polyn1] } {
00404         set polyn1 [polynomial $polyn1]
00405     }
00406     if { [llength $polyn2] == 1 && [string is double -strict $polyn2] } {
00407         set polyn2 [polynomial $polyn2]
00408     }
00409     if { [lindex $polyn1 0] != "POLYNOMIAL" ||
00410          [lindex $polyn2 0] != "POLYNOMIAL"    } {
00411         return -code error "Both arguments must be polynomials or a real number"
00412     }
00413 
00414     set coeffs1 [lindex $polyn1 1]
00415     set coeffs2 [lindex $polyn2 1]
00416 
00417     #
00418     # Take care of the null polynomial
00419     #
00420     if { $coeffs1 == {} } {
00421         return [polynomial {}]
00422     }
00423     if { $coeffs2 == {} } {
00424         return -code error "Denominator can not be zero"
00425     }
00426 
00427     foreach {quotient remainder} [DivRemPolyn $polyn1 $polyn2] {break}
00428     return $remainder
00429 }
00430 
00431 /*  DivRemPolyn --*/
00432 /*     Divide two polynomials and return the quotient and remainder*/
00433 /* */
00434 /*  Arguments:*/
00435 /*     polyn1       First polynomial or a scalar*/
00436 /*     polyn2       Second polynomial or a scalar*/
00437 /*  Result:*/
00438 /*     The difference of the two polynomials*/
00439 /*  Note:*/
00440 /*     Make sure that the first coefficient is not zero*/
00441 /* */
00442 ret  ::math::polynomials::DivRemPolyn (type polyn1 , type polyn2) {
00443 
00444     set coeffs1 [lindex $polyn1 1]
00445     set coeffs2 [lindex $polyn2 1]
00446 
00447     set steps [expr { [degreePolyn $polyn1] - [degreePolyn $polyn2] + 1 }]
00448 
00449     #
00450     # Special case: polynomial 1 has lower degree than polynomial 2
00451     #
00452     if { $steps <= 0 } {
00453         return [list [polynomial 0.0] $polyn1]
00454     } else {
00455         set extra_coeffs {}
00456         for { set i 1 } { $i < $steps } { incr i } {
00457             lappend extra_coeffs 0.0
00458         }
00459         lappend extra_coeffs 1.0
00460     }
00461 
00462     set c2 [lindex $coeffs2 0]
00463     set quot_coeffs {}
00464 
00465     for { set i 0 } { $i < $steps } { incr i } {
00466         set c1     [lindex $coeffs1 0]
00467         set factor [expr {$c1/$c2}]
00468 
00469         set fpolyn [multPolyn $polyn2 \
00470                               [polynomial [lrange $extra_coeffs $i end]]]
00471 
00472         set newpol [subPolyn $polyn1 [multPolyn $fpolyn $factor]]
00473 
00474         #
00475         # Due to rounding errors, a very small, parasitical
00476         # term may still exist. Remove it
00477         #
00478         if { [degreePolyn $newpol] == [degreePolyn $polyn1] } {
00479             set new_coeffs [lrange [allCoeffsPolyn $newpol] 0 end-1]
00480             set newpol     [polynomial $new_coeffs]
00481         }
00482         set polyn1 $newpol
00483         set coeffs1 [lindex $polyn1 1]
00484         set quot_coeffs [concat $factor $quot_coeffs]
00485     }
00486     set quotient [polynomial $quot_coeffs]
00487 
00488     return [list $quotient $polyn1]
00489 }
00490 
00491 /* */
00492 /*  Announce our presence*/
00493 /* */
00494 package provide math::polynomials 1.0.1
00495 
00496 /*  some tests --*/
00497 /* */
00498 if { 0 } {
00499  tcl = _precision 17
00500 
00501  f1 =     [::math::polynomials::polynomial {1 2 3}]
00502  f2 =     [::math::polynomials::polynomial {1 2 3 0}]
00503  f3 =     [::math::polynomials::polynomial {0 0 0 0}]
00504  f4 =     [::math::polynomials::polynomial {5 7}]
00505  cmdf1 =  [::math::polynomials::polynCmd {1 2 3}]
00506 
00507 foreach x {0 1 2 3 4 5} {
00508     puts "[::math::polynomials::evalPolyn $f1 $x] -- \
00509 [expr {1.0+2.0*$x+3.0*$x*$x}] -- \
00510 [$cmdf1 $x] -- [::math::polynomials::evalPolyn $f3 $x]"
00511 }
00512 
00513 puts "Degree: [::math::polynomials::degreePolyn $f1] (expected: 2)"
00514 puts "Degree: [::math::polynomials::degreePolyn $f2] (expected: 2)"
00515 foreach d {0 1 2} {
00516     puts "Coefficient $d = [::math::polynomials::coeffPolyn $f2 $d]"
00517 }
00518 puts "All coefficients = [::math::polynomials::allCoeffsPolyn $f2]"
00519 
00520 puts "Derivative = [::math::polynomials::derivPolyn $f1]"
00521 puts "Primitive  = [::math::polynomials::primitivePolyn $f1]"
00522 
00523 puts "Add:       [::math::polynomials::addPolyn $f1 $f4]"
00524 puts "Add:       [::math::polynomials::addPolyn $f4 $f1]"
00525 puts "Subtract:  [::math::polynomials::subPolyn $f1 $f4]"
00526 puts "Multiply:  [::math::polynomials::multPolyn $f1 $f4]"
00527 
00528  f1 =     [::math::polynomials::polynomial {1 2 3}]
00529  f2 =     [::math::polynomials::polynomial {0 1}]
00530 
00531 puts "Divide:    [::math::polynomials::divPolyn $f1 $f2]"
00532 puts "Remainder: [::math::polynomials::remainderPolyn $f1 $f2]"
00533 
00534  f1 =     [::math::polynomials::polynomial {1 2 3}]
00535  f2 =     [::math::polynomials::polynomial {1 1}]
00536 
00537 puts "Divide:    [::math::polynomials::divPolyn $f1 $f2]"
00538 puts "Remainder: [::math::polynomials::remainderPolyn $f1 $f2]"
00539 
00540  f1 =  [::math::polynomials::polynomial {1 2 3}]
00541  f2 =  [::math::polynomials::polynomial {0 1}]
00542  f3 =  [::math::polynomials::divPolyn $f2 $f1]
00543  coeffs =  [::math::polynomials::allCoeffsPolyn $f3]
00544 puts "Coefficients: $coeffs"
00545  f3 =  [::math::polynomials::divPolyn $f1 $f2]
00546  coeffs =  [::math::polynomials::allCoeffsPolyn $f3]
00547 puts "Coefficients: $coeffs"
00548  f1 =  [::math::polynomials::polynomial {1 2 3}]
00549  f2 =  [::math::polynomials::polynomial {0}]
00550  f3 =  [::math::polynomials::divPolyn $f2 $f1]
00551  coeffs =  [::math::polynomials::allCoeffsPolyn $f3]
00552 puts "Coefficients: $coeffs"
00553 }
00554 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1