Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

415 lines
11 KiB

  1. package Math::BigInt;
  2. use overload
  3. '+' => sub {new Math::BigInt &badd},
  4. '-' => sub {new Math::BigInt
  5. $_[2]? bsub($_[1],${$_[0]}) : bsub(${$_[0]},$_[1])},
  6. '<=>' => sub {new Math::BigInt
  7. $_[2]? bcmp($_[1],${$_[0]}) : bcmp(${$_[0]},$_[1])},
  8. 'cmp' => sub {new Math::BigInt
  9. $_[2]? ($_[1] cmp ${$_[0]}) : (${$_[0]} cmp $_[1])},
  10. '*' => sub {new Math::BigInt &bmul},
  11. '/' => sub {new Math::BigInt
  12. $_[2]? scalar bdiv($_[1],${$_[0]}) :
  13. scalar bdiv(${$_[0]},$_[1])},
  14. '%' => sub {new Math::BigInt
  15. $_[2]? bmod($_[1],${$_[0]}) : bmod(${$_[0]},$_[1])},
  16. '**' => sub {new Math::BigInt
  17. $_[2]? bpow($_[1],${$_[0]}) : bpow(${$_[0]},$_[1])},
  18. 'neg' => sub {new Math::BigInt &bneg},
  19. 'abs' => sub {new Math::BigInt &babs},
  20. qw(
  21. "" stringify
  22. 0+ numify) # Order of arguments unsignificant
  23. ;
  24. $NaNOK=1;
  25. sub new {
  26. my($class) = shift;
  27. my($foo) = bnorm(shift);
  28. die "Not a number initialized to Math::BigInt" if !$NaNOK && $foo eq "NaN";
  29. bless \$foo, $class;
  30. }
  31. sub stringify { "${$_[0]}" }
  32. sub numify { 0 + "${$_[0]}" } # Not needed, additional overhead
  33. # comparing to direct compilation based on
  34. # stringify
  35. sub import {
  36. shift;
  37. return unless @_;
  38. die "unknown import: @_" unless @_ == 1 and $_[0] eq ':constant';
  39. overload::constant integer => sub {Math::BigInt->new(shift)};
  40. }
  41. $zero = 0;
  42. # normalize string form of number. Strip leading zeros. Strip any
  43. # white space and add a sign, if missing.
  44. # Strings that are not numbers result the value 'NaN'.
  45. sub bnorm { #(num_str) return num_str
  46. local($_) = @_;
  47. s/\s+//g; # strip white space
  48. if (s/^([+-]?)0*(\d+)$/$1$2/) { # test if number
  49. substr($_,$[,0) = '+' unless $1; # Add missing sign
  50. s/^-0/+0/;
  51. $_;
  52. } else {
  53. 'NaN';
  54. }
  55. }
  56. # Convert a number from string format to internal base 100000 format.
  57. # Assumes normalized value as input.
  58. sub internal { #(num_str) return int_num_array
  59. local($d) = @_;
  60. ($is,$il) = (substr($d,$[,1),length($d)-2);
  61. substr($d,$[,1) = '';
  62. ($is, reverse(unpack("a" . ($il%5+1) . ("a5" x ($il/5)), $d)));
  63. }
  64. # Convert a number from internal base 100000 format to string format.
  65. # This routine scribbles all over input array.
  66. sub external { #(int_num_array) return num_str
  67. $es = shift;
  68. grep($_ > 9999 || ($_ = substr('0000'.$_,-5)), @_); # zero pad
  69. &bnorm(join('', $es, reverse(@_))); # reverse concat and normalize
  70. }
  71. # Negate input value.
  72. sub bneg { #(num_str) return num_str
  73. local($_) = &bnorm(@_);
  74. return $_ if $_ eq '+0' or $_ eq 'NaN';
  75. vec($_,0,8) ^= ord('+') ^ ord('-');
  76. $_;
  77. }
  78. # Returns the absolute value of the input.
  79. sub babs { #(num_str) return num_str
  80. &abs(&bnorm(@_));
  81. }
  82. sub abs { # post-normalized abs for internal use
  83. local($_) = @_;
  84. s/^-/+/;
  85. $_;
  86. }
  87. # Compares 2 values. Returns one of undef, <0, =0, >0. (suitable for sort)
  88. sub bcmp { #(num_str, num_str) return cond_code
  89. local($x,$y) = (&bnorm($_[$[]),&bnorm($_[$[+1]));
  90. if ($x eq 'NaN') {
  91. undef;
  92. } elsif ($y eq 'NaN') {
  93. undef;
  94. } else {
  95. &cmp($x,$y) <=> 0;
  96. }
  97. }
  98. sub cmp { # post-normalized compare for internal use
  99. local($cx, $cy) = @_;
  100. return 0 if ($cx eq $cy);
  101. local($sx, $sy) = (substr($cx, 0, 1), substr($cy, 0, 1));
  102. local($ld);
  103. if ($sx eq '+') {
  104. return 1 if ($sy eq '-' || $cy eq '+0');
  105. $ld = length($cx) - length($cy);
  106. return $ld if ($ld);
  107. return $cx cmp $cy;
  108. } else { # $sx eq '-'
  109. return -1 if ($sy eq '+');
  110. $ld = length($cy) - length($cx);
  111. return $ld if ($ld);
  112. return $cy cmp $cx;
  113. }
  114. }
  115. sub badd { #(num_str, num_str) return num_str
  116. local(*x, *y); ($x, $y) = (&bnorm($_[$[]),&bnorm($_[$[+1]));
  117. if ($x eq 'NaN') {
  118. 'NaN';
  119. } elsif ($y eq 'NaN') {
  120. 'NaN';
  121. } else {
  122. @x = &internal($x); # convert to internal form
  123. @y = &internal($y);
  124. local($sx, $sy) = (shift @x, shift @y); # get signs
  125. if ($sx eq $sy) {
  126. &external($sx, &add(*x, *y)); # if same sign add
  127. } else {
  128. ($x, $y) = (&abs($x),&abs($y)); # make abs
  129. if (&cmp($y,$x) > 0) {
  130. &external($sy, &sub(*y, *x));
  131. } else {
  132. &external($sx, &sub(*x, *y));
  133. }
  134. }
  135. }
  136. }
  137. sub bsub { #(num_str, num_str) return num_str
  138. &badd($_[$[],&bneg($_[$[+1]));
  139. }
  140. # GCD -- Euclids algorithm Knuth Vol 2 pg 296
  141. sub bgcd { #(num_str, num_str) return num_str
  142. local($x,$y) = (&bnorm($_[$[]),&bnorm($_[$[+1]));
  143. if ($x eq 'NaN' || $y eq 'NaN') {
  144. 'NaN';
  145. } else {
  146. ($x, $y) = ($y,&bmod($x,$y)) while $y ne '+0';
  147. $x;
  148. }
  149. }
  150. # routine to add two base 1e5 numbers
  151. # stolen from Knuth Vol 2 Algorithm A pg 231
  152. # there are separate routines to add and sub as per Kunth pg 233
  153. sub add { #(int_num_array, int_num_array) return int_num_array
  154. local(*x, *y) = @_;
  155. $car = 0;
  156. for $x (@x) {
  157. last unless @y || $car;
  158. $x -= 1e5 if $car = (($x += (@y ? shift(@y) : 0) + $car) >= 1e5) ? 1 : 0;
  159. }
  160. for $y (@y) {
  161. last unless $car;
  162. $y -= 1e5 if $car = (($y += $car) >= 1e5) ? 1 : 0;
  163. }
  164. (@x, @y, $car);
  165. }
  166. # subtract base 1e5 numbers -- stolen from Knuth Vol 2 pg 232, $x > $y
  167. sub sub { #(int_num_array, int_num_array) return int_num_array
  168. local(*sx, *sy) = @_;
  169. $bar = 0;
  170. for $sx (@sx) {
  171. last unless @sy || $bar;
  172. $sx += 1e5 if $bar = (($sx -= (@sy ? shift(@sy) : 0) + $bar) < 0);
  173. }
  174. @sx;
  175. }
  176. # multiply two numbers -- stolen from Knuth Vol 2 pg 233
  177. sub bmul { #(num_str, num_str) return num_str
  178. local(*x, *y); ($x, $y) = (&bnorm($_[$[]), &bnorm($_[$[+1]));
  179. if ($x eq 'NaN') {
  180. 'NaN';
  181. } elsif ($y eq 'NaN') {
  182. 'NaN';
  183. } else {
  184. @x = &internal($x);
  185. @y = &internal($y);
  186. &external(&mul(*x,*y));
  187. }
  188. }
  189. # multiply two numbers in internal representation
  190. # destroys the arguments, supposes that two arguments are different
  191. sub mul { #(*int_num_array, *int_num_array) return int_num_array
  192. local(*x, *y) = (shift, shift);
  193. local($signr) = (shift @x ne shift @y) ? '-' : '+';
  194. @prod = ();
  195. for $x (@x) {
  196. ($car, $cty) = (0, $[);
  197. for $y (@y) {
  198. $prod = $x * $y + ($prod[$cty] || 0) + $car;
  199. $prod[$cty++] =
  200. $prod - ($car = int($prod * 1e-5)) * 1e5;
  201. }
  202. $prod[$cty] += $car if $car;
  203. $x = shift @prod;
  204. }
  205. ($signr, @x, @prod);
  206. }
  207. # modulus
  208. sub bmod { #(num_str, num_str) return num_str
  209. (&bdiv(@_))[$[+1];
  210. }
  211. sub bdiv { #(dividend: num_str, divisor: num_str) return num_str
  212. local (*x, *y); ($x, $y) = (&bnorm($_[$[]), &bnorm($_[$[+1]));
  213. return wantarray ? ('NaN','NaN') : 'NaN'
  214. if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0');
  215. return wantarray ? ('+0',$x) : '+0' if (&cmp(&abs($x),&abs($y)) < 0);
  216. @x = &internal($x); @y = &internal($y);
  217. $srem = $y[$[];
  218. $sr = (shift @x ne shift @y) ? '-' : '+';
  219. $car = $bar = $prd = 0;
  220. if (($dd = int(1e5/($y[$#y]+1))) != 1) {
  221. for $x (@x) {
  222. $x = $x * $dd + $car;
  223. $x -= ($car = int($x * 1e-5)) * 1e5;
  224. }
  225. push(@x, $car); $car = 0;
  226. for $y (@y) {
  227. $y = $y * $dd + $car;
  228. $y -= ($car = int($y * 1e-5)) * 1e5;
  229. }
  230. }
  231. else {
  232. push(@x, 0);
  233. }
  234. @q = (); ($v2,$v1) = ($y[-2] || 0, $y[-1]);
  235. while ($#x > $#y) {
  236. ($u2,$u1,$u0) = ($x[-3] || 0, $x[-2] || 0, $x[-1]);
  237. $q = (($u0 == $v1) ? 99999 : int(($u0*1e5+$u1)/$v1));
  238. --$q while ($v2*$q > ($u0*1e5+$u1-$q*$v1)*1e5+$u2);
  239. if ($q) {
  240. ($car, $bar) = (0,0);
  241. for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) {
  242. $prd = $q * $y[$y] + $car;
  243. $prd -= ($car = int($prd * 1e-5)) * 1e5;
  244. $x[$x] += 1e5 if ($bar = (($x[$x] -= $prd + $bar) < 0));
  245. }
  246. if ($x[$#x] < $car + $bar) {
  247. $car = 0; --$q;
  248. for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) {
  249. $x[$x] -= 1e5
  250. if ($car = (($x[$x] += $y[$y] + $car) > 1e5));
  251. }
  252. }
  253. }
  254. pop(@x); unshift(@q, $q);
  255. }
  256. if (wantarray) {
  257. @d = ();
  258. if ($dd != 1) {
  259. $car = 0;
  260. for $x (reverse @x) {
  261. $prd = $car * 1e5 + $x;
  262. $car = $prd - ($tmp = int($prd / $dd)) * $dd;
  263. unshift(@d, $tmp);
  264. }
  265. }
  266. else {
  267. @d = @x;
  268. }
  269. (&external($sr, @q), &external($srem, @d, $zero));
  270. } else {
  271. &external($sr, @q);
  272. }
  273. }
  274. # compute power of two numbers -- stolen from Knuth Vol 2 pg 233
  275. sub bpow { #(num_str, num_str) return num_str
  276. local(*x, *y); ($x, $y) = (&bnorm($_[$[]), &bnorm($_[$[+1]));
  277. if ($x eq 'NaN') {
  278. 'NaN';
  279. } elsif ($y eq 'NaN') {
  280. 'NaN';
  281. } elsif ($x eq '+1') {
  282. '+1';
  283. } elsif ($x eq '-1') {
  284. &bmod($x,2) ? '-1': '+1';
  285. } elsif ($y =~ /^-/) {
  286. 'NaN';
  287. } elsif ($x eq '+0' && $y eq '+0') {
  288. 'NaN';
  289. } else {
  290. @x = &internal($x);
  291. local(@pow2)=@x;
  292. local(@pow)=&internal("+1");
  293. local($y1,$res,@tmp1,@tmp2)=(1); # need tmp to send to mul
  294. while ($y ne '+0') {
  295. ($y,$res)=&bdiv($y,2);
  296. if ($res ne '+0') {@tmp=@pow2; @pow=&mul(*pow,*tmp);}
  297. if ($y ne '+0') {@tmp=@pow2;@pow2=&mul(*pow2,*tmp);}
  298. }
  299. &external(@pow);
  300. }
  301. }
  302. 1;
  303. __END__
  304. =head1 NAME
  305. Math::BigInt - Arbitrary size integer math package
  306. =head1 SYNOPSIS
  307. use Math::BigInt;
  308. $i = Math::BigInt->new($string);
  309. $i->bneg return BINT negation
  310. $i->babs return BINT absolute value
  311. $i->bcmp(BINT) return CODE compare numbers (undef,<0,=0,>0)
  312. $i->badd(BINT) return BINT addition
  313. $i->bsub(BINT) return BINT subtraction
  314. $i->bmul(BINT) return BINT multiplication
  315. $i->bdiv(BINT) return (BINT,BINT) division (quo,rem) just quo if scalar
  316. $i->bmod(BINT) return BINT modulus
  317. $i->bgcd(BINT) return BINT greatest common divisor
  318. $i->bnorm return BINT normalization
  319. =head1 DESCRIPTION
  320. All basic math operations are overloaded if you declare your big
  321. integers as
  322. $i = new Math::BigInt '123 456 789 123 456 789';
  323. =over 2
  324. =item Canonical notation
  325. Big integer value are strings of the form C</^[+-]\d+$/> with leading
  326. zeros suppressed.
  327. =item Input
  328. Input values to these routines may be strings of the form
  329. C</^\s*[+-]?[\d\s]+$/>.
  330. =item Output
  331. Output values always always in canonical form
  332. =back
  333. Actual math is done in an internal format consisting of an array
  334. whose first element is the sign (/^[+-]$/) and whose remaining
  335. elements are base 100000 digits with the least significant digit first.
  336. The string 'NaN' is used to represent the result when input arguments
  337. are not numbers, as well as the result of dividing by zero.
  338. =head1 EXAMPLES
  339. '+0' canonical zero value
  340. ' -123 123 123' canonical value '-123123123'
  341. '1 23 456 7890' canonical value '+1234567890'
  342. =head1 Autocreating constants
  343. After C<use Math::BigInt ':constant'> all the integer decimal constants
  344. in the given scope are converted to C<Math::BigInt>. This conversion
  345. happens at compile time.
  346. In particular
  347. perl -MMath::BigInt=:constant -e 'print 2**100'
  348. print the integer value of C<2**100>. Note that without conversion of
  349. constants the expression 2**100 will be calculated as floating point number.
  350. =head1 BUGS
  351. The current version of this module is a preliminary version of the
  352. real thing that is currently (as of perl5.002) under development.
  353. =head1 AUTHOR
  354. Mark Biggar, overloaded interface by Ilya Zakharevich.
  355. =cut