Leaked source code of windows server 2003
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.

519 lines
14 KiB

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