bignum.tcl

Go to the documentation of this file.
00001 /*  bignum library in pure Tcl [VERSION 7Sep2004]*/
00002 /*  Copyright (C) 2004 Salvatore Sanfilippo <antirez at invece dot org>*/
00003 /*  Copyright (C) 2004 Arjen Markus <arjen dot markus at wldelft dot nl>*/
00004 /* */
00005 /*  LICENSE*/
00006 /* */
00007 /*  This software is:*/
00008 /*  Copyright (C) 2004 Salvatore Sanfilippo <antirez at invece dot org>*/
00009 /*  Copyright (C) 2004 Arjen Markus <arjen dot markus at wldelft dot nl>*/
00010 /*  The following terms apply to all files associated with the software*/
00011 /*  unless explicitly disclaimed in individual files.*/
00012 /* */
00013 /*  The authors hereby grant permission to use, copy, modify, distribute,*/
00014 /*  and license this software and its documentation for any purpose, provided*/
00015 /*  that existing copyright notices are retained in all copies and that this*/
00016 /*  notice is included verbatim in any distributions. No written agreement,*/
00017 /*  license, or royalty fee is required for any of the authorized uses.*/
00018 /*  Modifications to this software may be copyrighted by their authors*/
00019 /*  and need not follow the licensing terms described here, provided that*/
00020 /*  the new terms are clearly indicated on the first page of each file where*/
00021 /*  they apply.*/
00022 /* */
00023 /*  IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY*/
00024 /*  FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES*/
00025 /*  ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY*/
00026 /*  DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE*/
00027 /*  POSSIBILITY OF SUCH DAMAGE.*/
00028 /* */
00029 /*  THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,*/
00030 /*  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,*/
00031 /*  FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE*/
00032 /*  IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE*/
00033 /*  NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR*/
00034 /*  MODIFICATIONS.*/
00035 /* */
00036 /*  GOVERNMENT USE: If you are acquiring this software on behalf of the*/
00037 /*  U.S. government, the Government shall have only "Restricted Rights"*/
00038 /*  in the software and related documentation as defined in the Federal*/
00039 /*  Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you*/
00040 /*  are acquiring the software on behalf of the Department of Defense, the*/
00041 /*  software shall be classified as "Commercial Computer Software" and the*/
00042 /*  Government shall have only "Restricted Rights" as defined in Clause*/
00043 /*  252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the*/
00044 /*  authors grant the U.S. Government and others acting in its behalf*/
00045 /*  permission to use and distribute the software in accordance with the*/
00046 /*  terms specified in this license.*/
00047 
00048 /*  TODO*/
00049 /*  - pow and powm should check if the exponent is zero in order to return one*/
00050 
00051 package require Tcl 8.4
00052 
00053 namespace ::math::bignum {}
00054 
00055 /*  Misc ######################################*/
00056 
00057 /*  Don't change atombits define if you don't know what you are doing.*/
00058 /*  Note that it must be a power of two, and that 16 is too big*/
00059 /*  because expr may overflow in the product of two 16 bit numbers.*/
00060  ::math = ::bignum::atombits 16
00061  ::math = ::bignum::atombase [expr {1 << $::math::bignum::atombits}]
00062  ::math = ::bignum::atommask [expr {$::math::bignum::atombase-1}]
00063 
00064 /*  Note: to change 'atombits' is all you need to change the*/
00065 /*  library internal representation base.*/
00066 
00067 /*  Return the max between a and b (not bignums)*/
00068 ret  ::math::bignum::max (type a , type b) {
00069     expr {($a > $b) ? $a : $b}
00070 }
00071 
00072 /*  Return the min between a and b (not bignums)*/
00073 ret  ::math::bignum::min (type a , type b) {
00074     expr {($a < $b) ? $a : $b}
00075 }
00076 
00077 /*  Basic bignum operations ###########################*/
00078 
00079 /*  Returns a new bignum initialized to the value of 0.*/
00080 /* */
00081 /*  The big numbers are represented as a Tcl lists*/
00082 /*  The all-is-a-string representation does not pay here*/
00083 /*  bignums in Tcl are already slow, we can't slow-down it more.*/
00084 /* */
00085 /*  The bignum representation is [list bignum <sign> <atom0> ... <atomN>]*/
00086 /*  Where the atom0 is the least significant. Atoms are the digits*/
00087 /*  of a number in base 2^$::math::bignum::atombits*/
00088 /* */
00089 /*  The sign is 0 if the number is positive, 1 for negative numbers.*/
00090 
00091 /*  Note that the function accepts an argument used in order to*/
00092 /*  create a bignum of <atoms> atoms. For default zero is*/
00093 /*  represented as a single zero atom.*/
00094 /* */
00095 /*  The function is designed so that "set b [zero [atoms $a]]" will*/
00096 /*  produce 'b' with the same number of atoms as 'a'.*/
00097 ret  ::math::bignum::zero (optional value =0) {
00098     set v [list bignum 0 0]
00099     while { $value > 1 } {
00100        lappend v 0
00101        incr value -1
00102     }
00103     return $v
00104 }
00105 
00106 /*  Get the bignum sign*/
00107 ret  ::math::bignum::sign bignum (
00108     type lindex $, type bignum 1
00109 )
00110 
00111 # Get the number of atoms in the bignum
00112 proc ::math::bignum::atoms bignum {
00113     expr {[llength $bignum]-2}
00114 }
00115 
00116 /*  Get the i-th atom out of a bignum.*/
00117 /*  If the bignum is shorter than i atoms, the function*/
00118 /*  returns 0.*/
00119 ret  ::math::bignum::atom (type bignum , type i) {
00120     if {[::math::bignum::atoms $bignum] < [expr {$i+1}]} {
00121     return 0
00122     } else {
00123     lindex $bignum [expr {$i+2}]
00124     }
00125 }
00126 
00127 /*  Set the i-th atom out of a bignum. If the bignum*/
00128 /*  has less than 'i+1' atoms, add zero atoms to reach i.*/
00129 ret  ::math::bignum::setatom (type bignumvar , type i , type atomval) {
00130     upvar 1 $bignumvar bignum
00131     while {[::math::bignum::atoms $bignum] < [expr {$i+1}]} {
00132     lappend bignum 0
00133     }
00134     lset bignum [expr {$i+2}] $atomval
00135 }
00136 
00137 /*  Set the bignum sign*/
00138 ret  ::math::bignum::setsign (type bignumvar , type sign) {
00139     upvar 1 $bignumvar bignum
00140     lset bignum 1 $sign
00141 }
00142 
00143 /*  Remove trailing atoms with a value of zero*/
00144 /*  The normalized bignum is returned*/
00145 ret  ::math::bignum::normalize bignumvar (
00146     type upvar 1 $, type bignumvar , type bignum
00147     , type set , type atoms [, type expr , optional [llength =$bignum]-2]
00148     , type set , type i [, type expr , optional $atoms+1]
00149     , type while , optional $atoms =&& [lindex =$bignum $i] === 0 , optional 
00150     set =bignum [lrange =$bignum 0 =end-1]
00151     incr =atoms -1
00152     =incr i =-1
00153     
00154     , type if , optional !$atoms , optional 
00155     set =bignum [list =bignum 0 =0]
00156     
00157     , type return $, type bignum
00158 )
00159 
00160 # Return the absolute value of N
00161 proc ::math::bignum::abs n {
00162     ::math::bignum::setsign n 0
00163     return $n
00164 }
00165 
00166 /*  Comparison ###################################*/
00167 
00168 /*  Compare by absolute value. Called by ::math::bignum::cmp after the sign check.*/
00169 /* */
00170 /*  Returns 1 if |a| > |b|*/
00171 /*          0 if a == b*/
00172 /*         -1 if |a| < |b|*/
00173 /* */
00174 ret  ::math::bignum::abscmp (type a , type b) {
00175     if {[llength $a] > [llength $b]} {
00176     return 1
00177     } elseif {[llength $a] < [llength $b]} {
00178     return -1
00179     }
00180     set j [expr {[llength $a]-1}]
00181     while {$j >= 2} {
00182     if {[lindex $a $j] > [lindex $b $j]} {
00183         return 1
00184     } elseif {[lindex $a $j] < [lindex $b $j]} {
00185         return -1
00186     }
00187     incr j -1
00188     }
00189     return 0
00190 }
00191 
00192 /*  High level comparison. Return values:*/
00193 /* */
00194 /*   1 if a > b*/
00195 /*  -1 if a < b*/
00196 /*   0 if a == b*/
00197 /* */
00198 ret  ::math::bignum::cmp (type a , type b) { ; # same sign case
00199     set a [_treat $a]
00200     set b [_treat $b]
00201     if {[::math::bignum::sign $a] == [::math::bignum::sign $b]} {
00202     if {[::math::bignum::sign $a] == 0} {
00203         ::math::bignum::abscmp $a $b
00204     } else {
00205         expr {-([::math::bignum::abscmp $a $b])}
00206     }
00207     } else { ; # different sign case
00208     if {[::math::bignum::sign $a]} {return -1}
00209     return 1
00210     }
00211 }
00212 
00213 /*  Return true if 'z' is zero.*/
00214 ret  ::math::bignum::iszero z (
00215     type set , type z [_, type treat $, type z]
00216     , type expr , optional [llength =$z] == =3 && =[lindex $z =2] == =0
00217 )
00218 
00219 # Comparison facilities
00220 proc ::math::bignum::lt {a b} {expr {[::math::bignum::cmp $a $b] < 0}}
00221 ret  ::math::bignum::le (type a , type b) {expr {[::math::bignum::cmp $a $b] <= 0}}
00222 ret  ::math::bignum::gt (type a , type b) {expr {[::math::bignum::cmp $a $b] > 0}}
00223 ret  ::math::bignum::ge (type a , type b) {expr {[::math::bignum::cmp $a $b] >= 0}}
00224 ret  ::math::bignum::eq (type a , type b) {expr {[::math::bignum::cmp $a $b] == 0}}
00225 ret  ::math::bignum::ne (type a , type b) {expr {[::math::bignum::cmp $a $b] != 0}}
00226 
00227 /*  Addition / Subtraction #############################*/
00228 
00229 /*  Add two bignums, don't care about the sign.*/
00230 ret  ::math::bignum::rawAdd (type a , type b) {
00231     while {[llength $a] < [llength $b]} {lappend a 0}
00232     while {[llength $b] < [llength $a]} {lappend b 0}
00233     set r [::math::bignum::zero [expr {[llength $a]-1}]]
00234     set car 0
00235     for {set i 2} {$i < [llength $a]} {incr i} {
00236     set sum [expr {[lindex $a $i]+[lindex $b $i]+$car}]
00237     set car [expr {$sum >> $::math::bignum::atombits}]
00238     set sum [expr {$sum & $::math::bignum::atommask}]
00239     lset r $i $sum
00240     }
00241     if {$car} {
00242     lset r $i $car
00243     }
00244     ::math::bignum::normalize r
00245 }
00246 
00247 /*  Subtract two bignums, don't care about the sign. a > b condition needed.*/
00248 ret  ::math::bignum::rawSub (type a , type b) {
00249     set atoms [::math::bignum::atoms $a]
00250     set r [::math::bignum::zero $atoms]
00251     while {[llength $b] < [llength $a]} {lappend b 0} ; # b padding
00252     set car 0
00253     incr atoms 2
00254     for {set i 2} {$i < $atoms} {incr i} {
00255     set sub [expr {[lindex $a $i]-[lindex $b $i]-$car}]
00256     set car 0
00257     if {$sub < 0} {
00258         incr sub $::math::bignum::atombase
00259         set car 1
00260     }
00261     lset r $i $sub
00262     }
00263     # Note that if a > b there is no car in the last for iteration
00264     ::math::bignum::normalize r
00265 }
00266 
00267 /*  Higher level addition, care about sign and call rawAdd or rawSub*/
00268 /*  as needed.*/
00269 ret  ::math::bignum::add (type a , type b) {
00270     set a [_treat $a]
00271     set b [_treat $b]
00272     # Same sign case
00273     if {[::math::bignum::sign $a] == [::math::bignum::sign $b]} {
00274     set r [::math::bignum::rawAdd $a $b]
00275     ::math::bignum::setsign r [::math::bignum::sign $a]
00276     } else {
00277     # Different sign case
00278     set cmp [::math::bignum::abscmp $a $b]
00279     # 's' is the sign, set accordingly to A or B negative
00280     set s [expr {[::math::bignum::sign $a] == 1}]
00281     switch -- $cmp {
00282         0 {return [::math::bignum::zero]}
00283         1 {
00284         set r [::math::bignum::rawSub $a $b]
00285         ::math::bignum::setsign r $s
00286         return $r
00287         }
00288         -1 {
00289         set r [::math::bignum::rawSub $b $a]
00290         ::math::bignum::setsign r [expr {!$s}]
00291         return $r
00292         }
00293     }
00294     }
00295     return $r
00296 }
00297 
00298 /*  Higher level subtraction, care about sign and call rawAdd or rawSub*/
00299 /*  as needed.*/
00300 ret  ::math::bignum::sub (type a , type b) {
00301     set a [_treat $a]
00302     set b [_treat $b]
00303     # Different sign case
00304     if {[::math::bignum::sign $a] != [::math::bignum::sign $b]} {
00305     set r [::math::bignum::rawAdd $a $b]
00306     ::math::bignum::setsign r [::math::bignum::sign $a]
00307     } else {
00308     # Same sign case
00309     set cmp [::math::bignum::abscmp $a $b]
00310     # 's' is the sign, set accordingly to A and B both negative or positive
00311     set s [expr {[::math::bignum::sign $a] == 1}]
00312     switch -- $cmp {
00313         0 {return [::math::bignum::zero]}
00314         1 {
00315         set r [::math::bignum::rawSub $a $b]
00316         ::math::bignum::setsign r $s
00317         return $r
00318         }
00319         -1 {
00320         set r [::math::bignum::rawSub $b $a]
00321         ::math::bignum::setsign r [expr {!$s}]
00322         return $r
00323         }
00324     }
00325     }
00326     return $r
00327 }
00328 
00329 /*  Multiplication #################################*/
00330 
00331  ::math = ::bignum::karatsubaThreshold 32
00332 
00333 /*  Multiplication. Calls Karatsuba that calls Base multiplication under*/
00334 /*  a given threshold.*/
00335 ret  ::math::bignum::mul (type a , type b) {
00336     set a [_treat $a]
00337     set b [_treat $b]
00338     set r [::math::bignum::kmul $a $b]
00339     # The sign is the xor between the two signs
00340     ::math::bignum::setsign r [expr {[::math::bignum::sign $a]^[::math::bignum::sign $b]}]
00341 }
00342 
00343 /*  Karatsuba Multiplication*/
00344 ret  ::math::bignum::kmul (type a , type b) {
00345     set n [expr {[::math::bignum::max [llength $a] [llength $b]]-2}]
00346     set nmin [expr {[::math::bignum::min [llength $a] [llength $b]]-2}]
00347     if {$nmin < $::math::bignum::karatsubaThreshold} {return [::math::bignum::bmul $a $b]}
00348     set m [expr {($n+($n&1))/2}]
00349 
00350     set x0 [concat [list bignum 0] [lrange $a 2 [expr {$m+1}]]]
00351     set y0 [concat [list bignum 0] [lrange $b 2 [expr {$m+1}]]]
00352     set x1 [concat [list bignum 0] [lrange $a [expr {$m+2}] end]]
00353     set y1 [concat [list bignum 0] [lrange $b [expr {$m+2}] end]]
00354 
00355     if {0} {
00356     puts "m: $m"
00357     puts "x0: $x0"
00358     puts "x1: $x1"
00359     puts "y0: $y0"
00360     puts "y1: $y1"
00361     }
00362 
00363     set p1 [::math::bignum::kmul $x1 $y1]
00364     set p2 [::math::bignum::kmul $x0 $y0]
00365     set p3 [::math::bignum::kmul [::math::bignum::add $x1 $x0] [::math::bignum::add $y1 $y0]]
00366 
00367     set p3 [::math::bignum::sub $p3 $p1]
00368     set p3 [::math::bignum::sub $p3 $p2]
00369     set p1 [::math::bignum::lshiftAtoms $p1 [expr {$m*2}]]
00370     set p3 [::math::bignum::lshiftAtoms $p3 $m]
00371     set p3 [::math::bignum::add $p3 $p1]
00372     set p3 [::math::bignum::add $p3 $p2]
00373     return $p3
00374 }
00375 
00376 /*  Base Multiplication.*/
00377 ret  ::math::bignum::bmul (type a , type b) {
00378     set r [::math::bignum::zero [expr {[llength $a]+[llength $b]-3}]]
00379     for {set j 2} {$j < [llength $b]} {incr j} {
00380     set car 0
00381     set t [list bignum 0 0]
00382     for {set i 2} {$i < [llength $a]} {incr i} {
00383         # note that A = B * C + D + E
00384         # with A of N*2 bits and C,D,E of N bits
00385         # can't overflow since:
00386         # (2^N-1)*(2^N-1)+(2^N-1)+(2^N-1) == 2^(2*N)-1
00387         set t0 [lindex $a $i]
00388         set t1 [lindex $b $j]
00389         set t2 [lindex $r [expr {$i+$j-2}]]
00390         set mul [expr {wide($t0)*$t1+$t2+$car}]
00391         set car [expr {$mul >> $::math::bignum::atombits}]
00392         set mul [expr {$mul & $::math::bignum::atommask}]
00393         lset r [expr {$i+$j-2}] $mul
00394     }
00395     if {$car} {
00396         lset r [expr {$i+$j-2}] $car
00397     }
00398     }
00399     ::math::bignum::normalize r
00400 }
00401 
00402 /*  Shifting ####################################*/
00403 
00404 /*  Left shift 'z' of 'n' atoms. Low-level function used by ::math::bignum::lshift*/
00405 /*  Exploit the internal representation to go faster.*/
00406 ret  ::math::bignum::lshiftAtoms (type z , type n) {
00407     while {$n} {
00408     set z [linsert $z 2 0]
00409     incr n -1
00410     }
00411     return $z
00412 }
00413 
00414 /*  Right shift 'z' of 'n' atoms. Low-level function used by ::math::bignum::lshift*/
00415 /*  Exploit the internal representation to go faster.*/
00416 ret  ::math::bignum::rshiftAtoms (type z , type n) {
00417     set z [lreplace $z 2 [expr {$n+1}]]
00418 }
00419 
00420 /*  Left shift 'z' of 'n' bits. Low-level function used by ::math::bignum::lshift.*/
00421 /*  'n' must be <= $::math::bignum::atombits*/
00422 ret  ::math::bignum::lshiftBits (type z , type n) {
00423     set atoms [llength $z]
00424     set car 0
00425     for {set j 2} {$j < $atoms} {incr j} {
00426     set t [lindex $z $j]
00427     lset z $j \
00428         [expr {wide($car)|((wide($t)<<$n)&$::math::bignum::atommask)}]
00429     set car [expr {wide($t)>>($::math::bignum::atombits-$n)}]
00430     }
00431     if {$car} {
00432     lappend z 0
00433     lset z $j $car
00434     }
00435     return $z ; # No normalization needed
00436 }
00437 
00438 /*  Right shift 'z' of 'n' bits. Low-level function used by ::math::bignum::rshift.*/
00439 /*  'n' must be <= $::math::bignum::atombits*/
00440 ret  ::math::bignum::rshiftBits (type z , type n) {
00441     set atoms [llength $z]
00442     set car 0
00443     for {set j [expr {$atoms-1}]} {$j >= 2} {incr j -1} {
00444     set t [lindex $z $j]
00445     lset z $j [expr {wide($car)|(wide($t)>>$n)}]
00446     set car \
00447         [expr {(wide($t)<<($::math::bignum::atombits-$n))&$::math::bignum::atommask}]
00448     }
00449     ::math::bignum::normalize z
00450 }
00451 
00452 /*  Left shift 'z' of 'n' bits.*/
00453 ret  ::math::bignum::lshift (type z , type n) {
00454     set z [_treat $z]
00455     set atoms [expr {$n / $::math::bignum::atombits}]
00456     set bits [expr {$n & ($::math::bignum::atombits-1)}]
00457     ::math::bignum::lshiftBits [math::bignum::lshiftAtoms $z $atoms] $bits
00458 }
00459 
00460 /*  Right shift 'z' of 'n' bits.*/
00461 ret  ::math::bignum::rshift (type z , type n) {
00462     set z [_treat $z]
00463     set atoms [expr {$n / $::math::bignum::atombits}]
00464     set bits [expr {$n & ($::math::bignum::atombits-1)}]
00465 
00466     #
00467     # Correct for "arithmetic shift" - signed integers
00468     #
00469     set corr 0
00470     if { [::math::bignum::sign $z] == 1 } {
00471         for {set j [expr {$atoms+1}]} {$j >= 2} {incr j -1} {
00472             set t [lindex $z $j]
00473             if { $t != 0 } {
00474                 set corr 1
00475             }
00476         }
00477         if { $corr == 0 } {
00478             set t [lindex $z [expr {$atoms+2}]]
00479             if { ( $t & ~($::math::bignum::atommask<<($bits)) ) != 0 } {
00480                 set corr 1
00481             }
00482         }
00483     }
00484 
00485     set newz [::math::bignum::rshiftBits [math::bignum::rshiftAtoms $z $atoms] $bits]
00486     if { $corr } {
00487         set newz [::math::bignum::sub $newz 1]
00488     }
00489     return $newz
00490 }
00491 
00492 /*  Bit oriented ops ################################*/
00493 
00494 /*  Set the bit 'n' of 'bignumvar'*/
00495 ret  ::math::bignum::setbit (type bignumvar , type n) {
00496     upvar 1 $bignumvar z
00497     set atom [expr {$n / $::math::bignum::atombits}]
00498     set bit [expr {1 << ($n & ($::math::bignum::atombits-1))}]
00499     incr atom 2
00500     while {$atom >= [llength $z]} {lappend z 0}
00501     lset z $atom [expr {[lindex $z $atom]|$bit}]
00502 }
00503 
00504 /*  Clear the bit 'n' of 'bignumvar'*/
00505 ret  ::math::bignum::clearbit (type bignumvar , type n) {
00506     upvar 1 $bignumvar z
00507     set atom [expr {$n / $::math::bignum::atombits}]
00508     incr atom 2
00509     if {$atom >= [llength $z]} {return $z}
00510     set mask [expr {$::math::bignum::atommask^(1 << ($n & ($::math::bignum::atombits-1)))}]
00511     lset z $atom [expr {[lindex $z $atom]&$mask}]
00512     ::math::bignum::normalize z
00513 }
00514 
00515 /*  Test the bit 'n' of 'z'. Returns true if the bit is set.*/
00516 ret  ::math::bignum::testbit (type z , type n) {
00517     set  atom [expr {$n / $::math::bignum::atombits}]
00518     incr atom 2
00519     if {$atom >= [llength $z]} {return 0}
00520     set mask [expr {1 << ($n & ($::math::bignum::atombits-1))}]
00521     expr {([lindex $z $atom] & $mask) != 0}
00522 }
00523 
00524 /*  does bitwise and between a and b*/
00525 ret  ::math::bignum::bitand (type a , type b) {
00526     # The internal number rep is little endian. Appending zeros is
00527     # equivalent to adding leading zeros to a regular big-endian
00528     # representation. The two numbers are extended to the same length,
00529     # then the operation is applied to the absolute value.
00530     set a [_treat $a]
00531     set b [_treat $b]
00532     while {[llength $a] < [llength $b]} {lappend a 0}
00533     while {[llength $b] < [llength $a]} {lappend b 0}
00534     set r [::math::bignum::zero [expr {[llength $a]-1}]]
00535     for {set i 2} {$i < [llength $a]} {incr i} {
00536     set or [expr {[lindex $a $i] & [lindex $b $i]}]
00537     lset r $i $or
00538     }
00539     ::math::bignum::normalize r
00540 }
00541 
00542 /*  does bitwise XOR between a and b*/
00543 ret  ::math::bignum::bitxor (type a , type b) {
00544     # The internal number rep is little endian. Appending zeros is
00545     # equivalent to adding leading zeros to a regular big-endian
00546     # representation. The two numbers are extended to the same length,
00547     # then the operation is applied to the absolute value.
00548     set a [_treat $a]
00549     set b [_treat $b]
00550     while {[llength $a] < [llength $b]} {lappend a 0}
00551     while {[llength $b] < [llength $a]} {lappend b 0}
00552     set r [::math::bignum::zero [expr {[llength $a]-1}]]
00553     for {set i 2} {$i < [llength $a]} {incr i} {
00554     set or [expr {[lindex $a $i] ^ [lindex $b $i]}]
00555     lset r $i $or
00556     }
00557     ::math::bignum::normalize r
00558 }
00559 
00560 /*  does bitwise or between a and b*/
00561 ret  ::math::bignum::bitor (type a , type b) {
00562     # The internal number rep is little endian. Appending zeros is
00563     # equivalent to adding leading zeros to a regular big-endian
00564     # representation. The two numbers are extended to the same length,
00565     # then the operation is applied to the absolute value.
00566     set a [_treat $a]
00567     set b [_treat $b]
00568     while {[llength $a] < [llength $b]} {lappend a 0}
00569     while {[llength $b] < [llength $a]} {lappend b 0}
00570     set r [::math::bignum::zero [expr {[llength $a]-1}]]
00571     for {set i 2} {$i < [llength $a]} {incr i} {
00572     set or [expr {[lindex $a $i] | [lindex $b $i]}]
00573     lset r $i $or
00574     }
00575     ::math::bignum::normalize r
00576 }
00577 
00578 /*  Return the number of bits needed to represent 'z'.*/
00579 ret  ::math::bignum::bits z (
00580     type set , type atoms [::, type math::, type bignum::, type atoms $, type z]
00581     , type set , type bits [, type expr , optional ($atoms-1)*$::math::bignum::atombits]
00582     , type set , type atom [, type lindex $, type z [, type expr , optional $atoms+1]]
00583     , type while , optional $atom , optional 
00584     incr =bits
00585     set =atom [expr ={$atom >> =1]
00586     )
00587     return $bits
00588 }
00589 
00590 ################################## Division ####################################
00591 
00592 # Division. Returns [list n/d n%d]
00593 #
00594 # I got this algorithm from PGP 2.6.3i (see the mp_udiv function).
00595 # Here is how it works:
00596 #
00597 # Input:  N=(Nn,...,N2,N1,N0)radix2
00598 #         D=(Dn,...,D2,D1,D0)radix2
00599 # Output: Q=(Qn,...,Q2,Q1,Q0)radix2 = N/D
00600 #         R=(Rn,...,R2,R1,R0)radix2 = N%D
00601 #
00602 # Assume: N >= 0, D > 0
00603 #
00604 # For j from 0 to n
00605 #      Qj <- 0
00606 #      Rj <- 0
00607 # For j from n down to 0
00608 #      R <- R*2
00609 #      if Nj = 1 then R0 <- 1
00610 #      if R => D then R <- (R - D), Qn <- 1
00611 #
00612 # Note that the doubling of R is usually done leftshifting one position.
00613 # The only operations needed are bit testing, bit setting and subtraction.
00614 #
00615 # This is the "raw" version, don't care about the sign, returns both
00616 # quotient and rest as a two element list.
00617 # This procedure is used by divqr, div, mod, rem.
00618 proc ::math::bignum::rawDiv {n d} {
00619     set bit [expr {[::math::bignum::bits $n]-1}]
00620      r =  [list bignum 0 0]
00621      q =  [::math::bignum::zero [expr {[llength $n]-2}]]
00622     while {$bit >= 0} {
00623      b = _atom [expr {($bit / $::math::bignum::atombits) + 2}]
00624      b = _bit [expr {1 << ($bit & ($::math::bignum::atombits-1))}]
00625      r =  [::math::bignum::lshiftBits $r 1]
00626     if {[lindex $n $b_atom]&$b_bit} {
00627         l r =  2 [expr {[lindex $r 2] | 1}]
00628     }
00629     if {[::math::bignum::abscmp $r $d] >= 0} {
00630          r =  [::math::bignum::rawSub $r $d]
00631         l q =  $b_atom [expr {[lindex $q $b_atom]|$b_bit}]
00632     }
00633     incr bit -1
00634     }
00635     ::math::bignum::normalize q
00636     list $q $r
00637 }
00638 
00639 /*  Divide by single-atom immediate. Used to speedup bignum -> string conversion.*/
00640 /*  The procedure returns a two-elements list with the bignum quotient and*/
00641 /*  the remainder (that's just a number being <= of the max atom value).*/
00642 ret  ::math::bignum::rawDivByAtom (type n , type d) {
00643     set atoms [::math::bignum::atoms $n]
00644     set t 0
00645     set j $atoms
00646     incr j -1
00647     for {} {$j >= 0} {incr j -1} {
00648     set t [expr {($t << $::math::bignum::atombits)+[lindex $n [expr {$j+2}]]}]
00649     lset n [expr {$j+2}] [expr {$t/$d}]
00650     set t [expr {$t % $d}]
00651     }
00652     ::math::bignum::normalize n
00653     list $n $t
00654 }
00655 
00656 /*  Higher level division. Returns a list with two bignums, the first*/
00657 /*  is the quotient of n/d, the second the remainder n%d.*/
00658 /*  Note that if you want the *modulo* operator you should use ::math::bignum::mod*/
00659 /* */
00660 /*  The remainder sign is always the same as the divident.*/
00661 ret  ::math::bignum::divqr (type n , type d) {
00662     set n [_treat $n]
00663     set d [_treat $d]
00664     if {[::math::bignum::iszero $d]} {
00665     error "Division by zero"
00666     }
00667     foreach {q r} [::math::bignum::rawDiv $n $d] break
00668     ::math::bignum::setsign q [expr {[::math::bignum::sign $n]^[::math::bignum::sign $d]}]
00669     ::math::bignum::setsign r [::math::bignum::sign $n]
00670     list $q $r
00671 }
00672 
00673 /*  Like divqr, but only the quotient is returned.*/
00674 ret  ::math::bignum::div (type n , type d) {
00675     lindex [::math::bignum::divqr $n $d] 0
00676 }
00677 
00678 /*  Like divqr, but only the remainder is returned.*/
00679 ret  ::math::bignum::rem (type n , type d) {
00680     lindex [::math::bignum::divqr $n $d] 1
00681 }
00682 
00683 /*  Modular reduction. Returns N modulo M*/
00684 ret  ::math::bignum::mod (type n , type m) {
00685     set n [_treat $n]
00686     set m [_treat $m]
00687     set r [lindex [::math::bignum::divqr $n $m] 1]
00688     if {[::math::bignum::sign $m] != [::math::bignum::sign $r]} {
00689     set r [::math::bignum::add $r $m]
00690     }
00691     return $r
00692 }
00693 
00694 /*  Returns true if n is odd*/
00695 ret  ::math::bignum::isodd n (
00696     type expr , optional [lindex =$n 2]&1
00697 )
00698 
00699 # Returns true if n is even
00700 proc ::math::bignum::iseven n {
00701     expr {!([lindex $n 2]&1)}
00702 }
00703 
00704 /*  Power and Power mod N ############################*/
00705 
00706 /*  Returns b^e*/
00707 ret  ::math::bignum::pow (type b , type e) {
00708     set b [_treat $b]
00709     set e [_treat $e]
00710     if {[::math::bignum::iszero $e]} {return [list bignum 0 1]}
00711     # The power is negative is the base is negative and the exponent is odd
00712     set sign [expr {[::math::bignum::sign $b] && [::math::bignum::isodd $e]}]
00713     # Set the base to it's abs value, i.e. make it positive
00714     ::math::bignum::setsign b 0
00715     # Main loop
00716     set r [list bignum 0 1]; # Start with result = 1
00717     while {[::math::bignum::abscmp $e [list bignum 0 1]] > 0} { ;# While the exp > 1
00718     if {[::math::bignum::isodd $e]} {
00719         set r [::math::bignum::mul $r $b]
00720     }
00721     set e [::math::bignum::rshiftBits $e 1] ;# exp = exp / 2
00722     set b [::math::bignum::mul $b $b]
00723     }
00724     set r [::math::bignum::mul $r $b]
00725     ::math::bignum::setsign r $sign
00726     return $r
00727 }
00728 
00729 /*  Returns b^e mod m*/
00730 ret  ::math::bignum::powm (type b , type e , type m) {
00731     set b [_treat $b]
00732     set e [_treat $e]
00733     set m [_treat $m]
00734     if {[::math::bignum::iszero $e]} {return [list bignum 0 1]}
00735     # The power is negative is the base is negative and the exponent is odd
00736     set sign [expr {[::math::bignum::sign $b] && [::math::bignum::isodd $e]}]
00737     # Set the base to it's abs value, i.e. make it positive
00738     ::math::bignum::setsign b 0
00739     # Main loop
00740     set r [list bignum 0 1]; # Start with result = 1
00741     while {[::math::bignum::abscmp $e [list bignum 0 1]] > 0} { ;# While the exp > 1
00742     if {[::math::bignum::isodd $e]} {
00743         set r [::math::bignum::mod [::math::bignum::mul $r $b] $m]
00744     }
00745     set e [::math::bignum::rshiftBits $e 1] ;# exp = exp / 2
00746     set b [::math::bignum::mod [::math::bignum::mul $b $b] $m]
00747     }
00748     set r [::math::bignum::mul $r $b]
00749     ::math::bignum::setsign r $sign
00750     set r [::math::bignum::mod $r $m]
00751     return $r
00752 }
00753 
00754 /*  Square Root #################################*/
00755 
00756 /*  SQRT using the 'binary sqrt algorithm'.*/
00757 /* */
00758 /*  The basic algoritm consists in starting from the higer-bit*/
00759 /*  the real square root may have set, down to the bit zero,*/
00760 /*  trying to set every bit and checking if guess*guess is not*/
00761 /*  greater than 'n'. If it is greater we don't set the bit, otherwise*/
00762 /*  we set it. In order to avoid to compute guess*guess a trick*/
00763 /*  is used, so only addition and shifting are really required.*/
00764 ret  ::math::bignum::sqrt n (
00765     type if , optional [lindex =$n 1] , optional 
00766     error ="Square root =of a =negative number"
00767     
00768     , type set , type i [, type expr , optional (([::math::bignum::bits =$n]-1)/2)+1]
00769     , type set , type b [, type expr , optional $i*2] ; # , type Bit , type to , type set , type to , type get 2^, type i*2^, type i
00770 
00771     , type set , type r [::, type math::, type bignum::, type zero] ; # , type guess
00772     , type set , type x [::, type math::, type bignum::, type zero] ; # , type guess^2
00773     , type set , type s [::, type math::, type bignum::, type zero] ; # , type guess^2 , type backup
00774     , type set , type t [::, type math::, type bignum::, type zero] ; # , type intermediate , type result
00775     , type for , optional  , optional $i =>= 0 , optional incr =i -1; =incr b =-2 , optional 
00776     ::math::bignum::setbit =t $b
00777     =set x =[::math::bignum::rawAdd $s =$t]
00778     ::math::bignum::clearbit =t $b
00779     =if {[::math::bignum::abscmp =$x $n] =<= 0 , optional 
00780         set =s $x
00781         =::math::bignum::setbit r =$i
00782         ::math::bignum::setbit =t [expr ={$b+1]
00783     )
00784     set t [::math::bignum::rshiftBits $t 1]
00785     }
00786     return $r
00787 }
00788 
00789 ################################## Random Number ###############################
00790 
00791 # Returns a random number in the range [0,2^n-1]
00792 proc ::math::bignum::rand bits {
00793     set atoms [expr {($bits+$::math::bignum::atombits-1)/$::math::bignum::atombits}]
00794     set shift [expr {($atoms*$::math::bignum::atombits)-$bits}]
00795     set r [list bignum 0]
00796     while {$atoms} {
00797     lappend r [expr {int(rand()*(1<<$::math::bignum::atombits))}]
00798     incr atoms -1
00799     }
00800      r =  [::math::bignum::rshiftBits $r $shift]
00801     return $r
00802 }
00803 
00804 /*  Convertion to/from string #########################*/
00805 
00806 /*  The string representation charset. Max base is 36*/
00807  ::math = ::bignum::c "0123456789abcdefghijklmnopqrstuvwxyz = "
00808 
00809 /*  Convert 'z' to a string representation in base 'base'.*/
00810 /*  Note that this is missing a simple but very effective optimization*/
00811 /*  that's to divide by the biggest power of the base that fits*/
00812 /*  in a Tcl plain integer, and then to perform divisions with [expr].*/
00813 ret  ::math::bignum::tostr (type z , optional base =10) {
00814     if {[string length $::math::bignum::cset] < $base} {
00815     error "base too big for string convertion"
00816     }
00817     if {[::math::bignum::iszero $z]} {return 0}
00818     set sign [::math::bignum::sign $z]
00819     set str {}
00820     while {![::math::bignum::iszero $z]} {
00821     foreach {q r} [::math::bignum::rawDivByAtom $z $base] break
00822     append str [string index $::math::bignum::cset $r]
00823     set z $q
00824     }
00825     if {$sign} {append str -}
00826     # flip the resulting string
00827     set flipstr {}
00828     set i [string length $str]
00829     incr i -1
00830     while {$i >= 0} {
00831     append flipstr [string index $str $i]
00832     incr i -1
00833     }
00834     return $flipstr
00835 }
00836 
00837 /*  Create a bignum from a string representation in base 'base'.*/
00838 ret  ::math::bignum::fromstr (type str , optional base =0) {
00839     set z [::math::bignum::zero]
00840     set str [string trim $str]
00841     set sign 0
00842     if {[string index $str 0] eq {-}} {
00843     set str [string range $str 1 end]
00844     set sign 1
00845     }
00846     if {$base == 0} {
00847     switch -- [string tolower [string range $str 0 1]] {
00848         0x {set base 16; set str [string range $str 2 end]}
00849         ox {set base 8 ; set str [string range $str 2 end]}
00850         bx {set base 2 ; set str [string range $str 2 end]}
00851         default {set base 10}
00852     }
00853     }
00854     if {[string length $::math::bignum::cset] < $base} {
00855     error "base too big for string convertion"
00856     }
00857     set bigbase [list bignum 0 $base] ; # Build a bignum with the base value
00858     set basepow [list bignum 0 1] ; # multiply every digit for a succ. power
00859     set i [string length $str]
00860     incr i -1
00861     while {$i >= 0} {
00862     set digitval [string first [string index $str $i] $::math::bignum::cset]
00863     if {$digitval == -1} {
00864         error "Illegal char '[string index $str $i]' for base $base"
00865     }
00866     set bigdigitval [list bignum 0 $digitval]
00867     set z [::math::bignum::rawAdd $z [::math::bignum::mul $basepow $bigdigitval]]
00868     set basepow [::math::bignum::mul $basepow $bigbase]
00869     incr i -1
00870     }
00871     if {![::math::bignum::iszero $z]} {
00872     ::math::bignum::setsign z $sign
00873     }
00874     return $z
00875 }
00876 
00877 /* */
00878 /*  Pre-treatment of some constants : 0 and 1*/
00879 /*  Updated 19/11/2005 : abandon the 'upvar' command and its cost*/
00880 /* */
00881 ret  ::math::bignum::_treat (type num) {
00882     if {[llength $num]<2} {
00883         if {[string equal $num 0]} {
00884             # set to the bignum 0
00885             return {bignum 0 0}
00886         } elseif {[string equal $num 1]} {
00887             # set to the bignum 1
00888             return {bignum 0 1}
00889         }
00890     }
00891     return $num
00892 }
00893 
00894 namespace ::math::bignum {
00895     namespace export *
00896 }
00897 
00898 /*  Announce the package*/
00899 
00900 package provide math::bignum 3.1.1
00901 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1