polynomials.tcl
Go to the documentation of this file.00001
00002
00003
00004 namespace ::math::polynomials {
00005 variable count 0 ;
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
00017
00018
00019
00020
00021
00022
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
00047
00048
00049
00050
00051
00052
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
00071
00072
00073
00074
00075
00076
00077
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
00095
00096
00097
00098
00099
00100
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
00110
00111
00112
00113
00114
00115
00116
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
00130
00131
00132
00133
00134
00135
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
00150
00151
00152
00153
00154
00155
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
00172
00173
00174
00175
00176
00177
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
00194
00195
00196
00197
00198
00199
00200
00201
00202
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
00241
00242
00243
00244
00245
00246
00247
00248
00249
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
00288
00289
00290
00291
00292
00293
00294
00295
00296
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
00352
00353
00354
00355
00356
00357
00358
00359
00360
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
00392
00393
00394
00395
00396
00397
00398
00399
00400
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
00432
00433
00434
00435
00436
00437
00438
00439
00440
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
00493
00494 package provide math::polynomials 1.0.1
00495
00496
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