This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade to Math::BigRat 0.11
[perl5.git] / lib / Math / BigRat.pm
CommitLineData
a4e2b1c6
JH
1
2#
7d341013 3# "Tax the rat farms." - Lord Vetinari
a4e2b1c6 4#
184f15d5
JH
5
6# The following hash values are used:
7# sign : +,-,NaN,+inf,-inf
8# _d : denominator
9# _n : numeraotr (value = _n/_d)
10# _a : accuracy
11# _p : precision
b1f79218 12# _f : flags, used by MBR to flag parts of a rational as untouchable
7afd7a91 13# You should not look at the innards of a BigRat - use the methods for this.
184f15d5
JH
14
15package Math::BigRat;
16
a4e2b1c6 17require 5.005_03;
184f15d5
JH
18use strict;
19
7afd7a91 20require Exporter;
184f15d5
JH
21use Math::BigFloat;
22use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK $upgrade $downgrade
990fb837 23 $accuracy $precision $round_mode $div_scale $_trap_nan $_trap_inf);
184f15d5
JH
24
25@ISA = qw(Exporter Math::BigFloat);
26@EXPORT_OK = qw();
27
7afd7a91 28$VERSION = '0.11';
184f15d5 29
990fb837 30use overload; # inherit from Math::BigFloat
184f15d5
JH
31
32##############################################################################
33# global constants, flags and accessory
34
184f15d5
JH
35$accuracy = $precision = undef;
36$round_mode = 'even';
37$div_scale = 40;
38$upgrade = undef;
39$downgrade = undef;
40
990fb837
RGS
41# these are internally, and not to be used from the outside
42
43use constant MB_NEVER_ROUND => 0x0001;
44
45$_trap_nan = 0; # are NaNs ok? set w/ config()
46$_trap_inf = 0; # are infs ok? set w/ config()
47
184f15d5
JH
48my $nan = 'NaN';
49my $class = 'Math::BigRat';
6de7f0cc 50my $MBI = 'Math::BigInt';
184f15d5 51
8f675a64
JH
52sub isa
53 {
54 return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't
55 UNIVERSAL::isa(@_);
56 }
57
184f15d5
JH
58sub _new_from_float
59 {
7afd7a91 60 # turn a single float input into a rational number (like '0.1')
184f15d5
JH
61 my ($self,$f) = @_;
62
63 return $self->bnan() if $f->is_nan();
64 return $self->binf('-inf') if $f->{sign} eq '-inf';
65 return $self->binf('+inf') if $f->{sign} eq '+inf';
66
184f15d5 67 $self->{_n} = $f->{_m}->copy(); # mantissa
6de7f0cc 68 $self->{_d} = $MBI->bone();
990fb837 69 $self->{sign} = $f->{sign} || '+'; $self->{_n}->{sign} = '+';
184f15d5
JH
70 if ($f->{_e}->{sign} eq '-')
71 {
72 # something like Math::BigRat->new('0.1');
73 $self->{_d}->blsft($f->{_e}->copy()->babs(),10); # 1 / 1 => 1/10
74 }
75 else
76 {
77 # something like Math::BigRat->new('10');
78 # 1 / 1 => 10/1
79 $self->{_n}->blsft($f->{_e},10) unless $f->{_e}->is_zero();
80 }
184f15d5
JH
81 $self;
82 }
83
84sub new
85 {
86 # create a Math::BigRat
87 my $class = shift;
88
89 my ($n,$d) = shift;
90
91 my $self = { }; bless $self,$class;
92
184f15d5
JH
93 # input like (BigInt,BigInt) or (BigFloat,BigFloat) not handled yet
94
6de7f0cc 95 if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat')))
184f15d5 96 {
184f15d5
JH
97 if ($n->isa('Math::BigFloat'))
98 {
7afd7a91 99 $self->_new_from_float($n);
184f15d5
JH
100 }
101 if ($n->isa('Math::BigInt'))
102 {
990fb837 103 # TODO: trap NaN, inf
8f675a64 104 $self->{_n} = $n->copy(); # "mantissa" = $n
6de7f0cc 105 $self->{_d} = $MBI->bone();
8f675a64 106 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
8f675a64
JH
107 }
108 if ($n->isa('Math::BigInt::Lite'))
109 {
990fb837
RGS
110 # TODO: trap NaN, inf
111 $self->{sign} = '+'; $self->{sign} = '-' if $$n < 0;
112 $self->{_n} = $MBI->new(abs($$n),undef,undef); # "mantissa" = $n
6de7f0cc 113 $self->{_d} = $MBI->bone();
184f15d5 114 }
7afd7a91 115 return $self->bnorm();
184f15d5
JH
116 }
117 return $n->copy() if ref $n;
184f15d5
JH
118
119 if (!defined $n)
120 {
7d341013 121 $self->{_n} = $MBI->bzero(); # undef => 0
6de7f0cc 122 $self->{_d} = $MBI->bone();
184f15d5
JH
123 $self->{sign} = '+';
124 return $self->bnorm();
125 }
126 # string input with / delimiter
127 if ($n =~ /\s*\/\s*/)
128 {
990fb837
RGS
129 return $class->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid
130 return $class->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid
184f15d5
JH
131 ($n,$d) = split (/\//,$n);
132 # try as BigFloats first
133 if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/))
134 {
135 # one of them looks like a float
7d341013
T
136 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so:
137 local $Math::BigFloat::accuracy = undef;
138 local $Math::BigFloat::precision = undef;
139 local $Math::BigInt::accuracy = undef;
140 local $Math::BigInt::precision = undef;
990fb837
RGS
141 my $nf = Math::BigFloat->new($n);
142 $self->{sign} = '+';
143 return $self->bnan() if $nf->is_nan();
144 $self->{_n} = $nf->{_m};
184f15d5 145 # now correct $self->{_n} due to $n
7d341013 146 my $f = Math::BigFloat->new($d,undef,undef);
990fb837
RGS
147 $self->{_d} = $f->{_m};
148 return $self->bnan() if $f->is_nan();
149 #print "n=$nf e$nf->{_e} d=$f e$f->{_e}\n";
150 # calculate the difference between nE and dE
151 my $diff_e = $nf->{_e}->copy()->bsub ( $f->{_e} );
152 if ($diff_e->is_negative())
153 {
154 # < 0: mul d with it
155 $self->{_d}->blsft($diff_e->babs(),10);
156 }
157 elsif (!$diff_e->is_zero())
184f15d5 158 {
990fb837
RGS
159 # > 0: mul n with it
160 $self->{_n}->blsft($diff_e,10);
184f15d5 161 }
184f15d5
JH
162 }
163 else
164 {
7d341013
T
165 # both d and n are (big)ints
166 $self->{_n} = $MBI->new($n,undef,undef);
167 $self->{_d} = $MBI->new($d,undef,undef);
990fb837
RGS
168 $self->{sign} = '+';
169 return $self->bnan() if $self->{_n}->{sign} eq $nan ||
170 $self->{_d}->{sign} eq $nan;
93c87d9d 171 # handle inf and NAN cases:
990fb837
RGS
172 if ($self->{_n}->is_inf() || $self->{_d}->is_inf())
173 {
174 # inf/inf => NaN
175 return $self->bnan() if
176 ($self->{_n}->is_inf() && $self->{_d}->is_inf());
7afd7a91
T
177 if ($self->{_n}->is_inf())
178 {
179 my $s = '+'; # '+inf/+123' or '-inf/-123'
180 $s = '-' if substr($self->{_n}->{sign},0,1) ne $self->{_d}->{sign};
181 # +-inf/123 => +-inf
182 return $self->binf($s);
183 }
990fb837
RGS
184 # 123/inf => 0
185 return $self->bzero();
186 }
184f15d5 187
990fb837 188 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->babs();
184f15d5
JH
189 # if $d is negative, flip sign
190 $self->{sign} =~ tr/+-/-+/ if $self->{_d}->{sign} eq '-';
990fb837 191 $self->{_d}->babs(); # normalize
184f15d5 192 }
990fb837 193
184f15d5
JH
194 return $self->bnorm();
195 }
196
197 # simple string input
198 if (($n =~ /[\.eE]/))
199 {
7d341013
T
200 # looks like a float, quacks like a float, so probably is a float
201 # Math::BigFloat($n,undef,undef) does not what it is supposed to do, so:
202 local $Math::BigFloat::accuracy = undef;
203 local $Math::BigFloat::precision = undef;
204 local $Math::BigInt::accuracy = undef;
205 local $Math::BigInt::precision = undef;
990fb837 206 $self->{sign} = 'NaN';
7d341013 207 $self->_new_from_float(Math::BigFloat->new($n,undef,undef));
184f15d5
JH
208 }
209 else
210 {
7d341013 211 $self->{_n} = $MBI->new($n,undef,undef);
6de7f0cc 212 $self->{_d} = $MBI->bone();
990fb837 213 $self->{sign} = $self->{_n}->{sign}; $self->{_n}->babs();
a4e2b1c6
JH
214 return $self->bnan() if $self->{sign} eq 'NaN';
215 return $self->binf($self->{sign}) if $self->{sign} =~ /^[+-]inf$/;
184f15d5
JH
216 }
217 $self->bnorm();
218 }
219
990fb837
RGS
220##############################################################################
221
222sub config
223 {
224 # return (later set?) configuration data as hash ref
225 my $class = shift || 'Math::BigFloat';
226
227 my $cfg = $class->SUPER::config(@_);
228
229 # now we need only to override the ones that are different from our parent
230 $cfg->{class} = $class;
231 $cfg->{with} = $MBI;
232 $cfg;
233 }
234
235##############################################################################
8f675a64 236
184f15d5
JH
237sub bstr
238 {
7afd7a91 239 my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
184f15d5
JH
240
241 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
242 {
243 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
244 return $s;
245 }
246
7afd7a91 247 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # '+3/2' => '3/2'
184f15d5 248
7afd7a91
T
249 return $s . $x->{_n}->bstr() if $x->{_d}->is_one();
250 $s . $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
184f15d5
JH
251 }
252
253sub bsstr
254 {
255 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
256
257 if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
258 {
259 my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
260 return $s;
261 }
262
263 my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
7afd7a91 264 $s . $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
184f15d5
JH
265 }
266
267sub bnorm
268 {
269 # reduce the number to the shortest form and remember this (so that we
270 # don't reduce again)
271 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
272
990fb837
RGS
273 # both parts must be BigInt's (or whatever we are using today)
274 if (ref($x->{_n}) ne $MBI)
275 {
276 require Carp; Carp::croak ("n is not $MBI but (".ref($x->{_n}).')');
277 }
278 if (ref($x->{_d}) ne $MBI)
279 {
280 require Carp; Carp::croak ("d is not $MBI but (".ref($x->{_d}).')');
281 }
6de7f0cc 282
184f15d5
JH
283 # this is to prevent automatically rounding when MBI's globals are set
284 $x->{_d}->{_f} = MB_NEVER_ROUND;
285 $x->{_n}->{_f} = MB_NEVER_ROUND;
286 # 'forget' that parts were rounded via MBI::bround() in MBF's bfround()
7afd7a91
T
287 delete $x->{_d}->{_a}; delete $x->{_n}->{_a};
288 delete $x->{_d}->{_p}; delete $x->{_n}->{_p};
184f15d5 289
6de7f0cc
JH
290 # no normalize for NaN, inf etc.
291 return $x if $x->{sign} !~ /^[+-]$/;
292
184f15d5
JH
293 # normalize zeros to 0/1
294 if (($x->{sign} =~ /^[+-]$/) &&
295 ($x->{_n}->is_zero()))
296 {
a4e2b1c6 297 $x->{sign} = '+'; # never -0
6de7f0cc 298 $x->{_d} = $MBI->bone() unless $x->{_d}->is_one();
184f15d5
JH
299 return $x;
300 }
301
a4e2b1c6 302 return $x if $x->{_d}->is_one(); # no need to reduce
6de7f0cc 303
184f15d5 304 # reduce other numbers
8f675a64
JH
305 # disable upgrade in BigInt, otherwise deep recursion
306 local $Math::BigInt::upgrade = undef;
7d341013
T
307 local $Math::BigInt::accuracy = undef;
308 local $Math::BigInt::precision = undef;
184f15d5
JH
309 my $gcd = $x->{_n}->bgcd($x->{_d});
310
311 if (!$gcd->is_one())
312 {
313 $x->{_n}->bdiv($gcd);
314 $x->{_d}->bdiv($gcd);
315 }
184f15d5
JH
316 $x;
317 }
318
319##############################################################################
320# special values
321
322sub _bnan
323 {
990fb837 324 # used by parent class bnan() to initialize number to NaN
184f15d5 325 my $self = shift;
990fb837
RGS
326
327 if ($_trap_nan)
328 {
329 require Carp;
330 my $class = ref($self);
331 Carp::croak ("Tried to set $self to NaN in $class\::_bnan()");
332 }
a4e2b1c6
JH
333 $self->{_n} = $MBI->bzero();
334 $self->{_d} = $MBI->bzero();
184f15d5
JH
335 }
336
337sub _binf
338 {
7d341013 339 # used by parent class bone() to initialize number to +inf/-inf
184f15d5 340 my $self = shift;
990fb837
RGS
341
342 if ($_trap_inf)
343 {
344 require Carp;
345 my $class = ref($self);
346 Carp::croak ("Tried to set $self to inf in $class\::_binf()");
347 }
a4e2b1c6
JH
348 $self->{_n} = $MBI->bzero();
349 $self->{_d} = $MBI->bzero();
184f15d5
JH
350 }
351
352sub _bone
353 {
7d341013 354 # used by parent class bone() to initialize number to +1/-1
184f15d5 355 my $self = shift;
a4e2b1c6
JH
356 $self->{_n} = $MBI->bone();
357 $self->{_d} = $MBI->bone();
184f15d5
JH
358 }
359
360sub _bzero
361 {
990fb837 362 # used by parent class bzero() to initialize number to 0
184f15d5 363 my $self = shift;
a4e2b1c6
JH
364 $self->{_n} = $MBI->bzero();
365 $self->{_d} = $MBI->bone();
184f15d5
JH
366 }
367
368##############################################################################
369# mul/add/div etc
370
371sub badd
372 {
7afd7a91 373 # add two rational numbers
7d341013
T
374
375 # set up parameters
376 my ($self,$x,$y,@r) = (ref($_[0]),@_);
377 # objectify is costly, so avoid it
378 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
379 {
380 ($self,$x,$y,@r) = objectify(2,@_);
381 }
184f15d5 382
6de7f0cc
JH
383 $x = $self->new($x) unless $x->isa($self);
384 $y = $self->new($y) unless $y->isa($self);
184f15d5 385
8f675a64 386 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
7d341013 387 # TODO: inf handling
184f15d5
JH
388
389 # 1 1 gcd(3,4) = 1 1*3 + 1*4 7
390 # - + - = --------- = --
391 # 4 3 4*3 12
392
7d341013
T
393 # we do not compute the gcd() here, but simple do:
394 # 5 7 5*3 + 7*4 41
395 # - + - = --------- = --
396 # 4 3 4*3 12
397
398 # the gcd() calculation and reducing is then done in bnorm()
184f15d5 399
7d341013
T
400 local $Math::BigInt::accuracy = undef;
401 local $Math::BigInt::precision = undef;
402
403 $x->{_n}->bmul($y->{_d}); $x->{_n}->{sign} = $x->{sign};
404 my $m = $y->{_n}->copy()->bmul($x->{_d});
184f15d5
JH
405 $m->{sign} = $y->{sign}; # 2/1 - 2/1
406 $x->{_n}->badd($m);
407
408 $x->{_d}->bmul($y->{_d});
409
7afd7a91 410 # calculate sign of result and norm our _n part
184f15d5
JH
411 $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
412
7d341013 413 $x->bnorm()->round(@r);
184f15d5
JH
414 }
415
416sub bsub
417 {
7afd7a91 418 # subtract two rational numbers
7d341013
T
419
420 # set up parameters
421 my ($self,$x,$y,@r) = (ref($_[0]),@_);
422 # objectify is costly, so avoid it
423 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
424 {
425 ($self,$x,$y,@r) = objectify(2,@_);
426 }
184f15d5 427
7afd7a91
T
428 # flip sign of $x, call badd(), then flip sign of result
429 $x->{sign} =~ tr/+-/-+/
430 unless $x->{sign} eq '+' && $x->{_n}->is_zero(); # not -0
431 $x->badd($y,@r); # does norm and round
432 $x->{sign} =~ tr/+-/-+/
433 unless $x->{sign} eq '+' && $x->{_n}->is_zero(); # not -0
434 $x;
184f15d5
JH
435 }
436
437sub bmul
438 {
7afd7a91 439 # multiply two rational numbers
7d341013
T
440
441 # set up parameters
442 my ($self,$x,$y,@r) = (ref($_[0]),@_);
443 # objectify is costly, so avoid it
444 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
445 {
446 ($self,$x,$y,@r) = objectify(2,@_);
447 }
184f15d5 448
990fb837 449 # TODO: $self instead or $class??
8f675a64
JH
450 $x = $class->new($x) unless $x->isa($class);
451 $y = $class->new($y) unless $y->isa($class);
452
184f15d5
JH
453 return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
454
455 # inf handling
456 if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/))
457 {
458 return $x->bnan() if $x->is_zero() || $y->is_zero();
459 # result will always be +-inf:
460 # +inf * +/+inf => +inf, -inf * -/-inf => +inf
461 # +inf * -/-inf => -inf, -inf * +/+inf => -inf
462 return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/);
463 return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
464 return $x->binf('-');
465 }
466
467 # x== 0 # also: or y == 1 or y == -1
468 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
469
184f15d5
JH
470 # According to Knuth, this can be optimized by doingtwice gcd (for d and n)
471 # and reducing in one step)
472
473 # 1 1 2 1
474 # - * - = - = -
475 # 4 3 12 6
7d341013
T
476
477 local $Math::BigInt::accuracy = undef;
478 local $Math::BigInt::precision = undef;
184f15d5
JH
479 $x->{_n}->bmul($y->{_n});
480 $x->{_d}->bmul($y->{_d});
481
482 # compute new sign
483 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
484
7d341013 485 $x->bnorm()->round(@r);
184f15d5
JH
486 }
487
488sub bdiv
489 {
490 # (dividend: BRAT or num_str, divisor: BRAT or num_str) return
491 # (BRAT,BRAT) (quo,rem) or BRAT (only rem)
7d341013
T
492
493 # set up parameters
494 my ($self,$x,$y,@r) = (ref($_[0]),@_);
495 # objectify is costly, so avoid it
496 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
497 {
498 ($self,$x,$y,@r) = objectify(2,@_);
499 }
184f15d5 500
990fb837 501 # TODO: $self instead or $class??
8f675a64
JH
502 $x = $class->new($x) unless $x->isa($class);
503 $y = $class->new($y) unless $y->isa($class);
504
184f15d5
JH
505 return $self->_div_inf($x,$y)
506 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
507
508 # x== 0 # also: or y == 1 or y == -1
509 return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
510
511 # TODO: list context, upgrade
512
184f15d5
JH
513 # 1 1 1 3
514 # - / - == - * -
515 # 4 3 4 1
7d341013
T
516
517# local $Math::BigInt::accuracy = undef;
518# local $Math::BigInt::precision = undef;
184f15d5
JH
519 $x->{_n}->bmul($y->{_d});
520 $x->{_d}->bmul($y->{_n});
521
522 # compute new sign
523 $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
524
7d341013 525 $x->bnorm()->round(@r);
6de7f0cc 526 $x;
184f15d5
JH
527 }
528
990fb837
RGS
529sub bmod
530 {
531 # compute "remainder" (in Perl way) of $x / $y
532
533 # set up parameters
534 my ($self,$x,$y,@r) = (ref($_[0]),@_);
535 # objectify is costly, so avoid it
536 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
537 {
538 ($self,$x,$y,@r) = objectify(2,@_);
539 }
540
541 # TODO: $self instead or $class??
542 $x = $class->new($x) unless $x->isa($class);
543 $y = $class->new($y) unless $y->isa($class);
544
545 return $self->_div_inf($x,$y)
546 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
547
548 return $self->_div_inf($x,$y)
549 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
550
551 return $x if $x->is_zero(); # 0 / 7 = 0, mod 0
552
553 # compute $x - $y * floor($x/$y), keeping the sign of $x
554
93c87d9d 555 # locally disable these, since they would interfere
990fb837
RGS
556 local $Math::BigInt::upgrade = undef;
557 local $Math::BigInt::accuracy = undef;
558 local $Math::BigInt::precision = undef;
559
560 my $u = $x->copy()->babs();
93c87d9d 561 # first, do a "normal" division ($x/$y)
990fb837
RGS
562 $u->{_d}->bmul($y->{_n});
563 $u->{_n}->bmul($y->{_d});
564
565 # compute floor
566 if (!$u->{_d}->is_one())
567 {
568 $u->{_n}->bdiv($u->{_d}); # 22/7 => 3/1 w/ truncate
569 # no need to set $u->{_d} to 1, since later we set it to $y->{_d}
570 #$x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
571 }
572
573 # compute $y * $u
574 $u->{_d} = $y->{_d}; # 1 * $y->{_d}, see floor above
575 $u->{_n}->bmul($y->{_n});
576
577 my $xsign = $x->{sign}; $x->{sign} = '+'; # remember sign and make abs
578 # compute $x - $u
579 $x->bsub($u);
580 $x->{sign} = $xsign; # put sign back
581
582 $x->bnorm()->round(@r);
990fb837
RGS
583 }
584
184f15d5 585##############################################################################
a4e2b1c6
JH
586# bdec/binc
587
588sub bdec
589 {
590 # decrement value (subtract 1)
591 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
592
593 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
594
595 if ($x->{sign} eq '-')
596 {
597 $x->{_n}->badd($x->{_d}); # -5/2 => -7/2
598 }
599 else
600 {
601 if ($x->{_n}->bacmp($x->{_d}) < 0)
602 {
603 # 1/3 -- => -2/3
604 $x->{_n} = $x->{_d} - $x->{_n};
605 $x->{sign} = '-';
606 }
607 else
608 {
609 $x->{_n}->bsub($x->{_d}); # 5/2 => 3/2
610 }
611 }
612 $x->bnorm()->round(@r);
a4e2b1c6
JH
613 }
614
615sub binc
616 {
617 # increment value (add 1)
618 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
619
620 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
621
622 if ($x->{sign} eq '-')
623 {
624 if ($x->{_n}->bacmp($x->{_d}) < 0)
625 {
626 # -1/3 ++ => 2/3 (overflow at 0)
627 $x->{_n} = $x->{_d} - $x->{_n};
628 $x->{sign} = '+';
629 }
630 else
631 {
632 $x->{_n}->bsub($x->{_d}); # -5/2 => -3/2
633 }
634 }
635 else
636 {
637 $x->{_n}->badd($x->{_d}); # 5/2 => 7/2
638 }
639 $x->bnorm()->round(@r);
a4e2b1c6
JH
640 }
641
642##############################################################################
184f15d5
JH
643# is_foo methods (the rest is inherited)
644
645sub is_int
646 {
647 # return true if arg (BRAT or num_str) is an integer
648 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
649
650 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't
7d341013 651 $x->{_d}->is_one(); # x/y && y != 1 => no integer
184f15d5
JH
652 0;
653 }
654
655sub is_zero
656 {
657 # return true if arg (BRAT or num_str) is zero
658 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
659
660 return 1 if $x->{sign} eq '+' && $x->{_n}->is_zero();
661 0;
662 }
663
664sub is_one
665 {
666 # return true if arg (BRAT or num_str) is +1 or -1 if signis given
667 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
668
669 my $sign = shift || ''; $sign = '+' if $sign ne '-';
670 return 1
671 if ($x->{sign} eq $sign && $x->{_n}->is_one() && $x->{_d}->is_one());
672 0;
673 }
674
675sub is_odd
676 {
677 # return true if arg (BFLOAT or num_str) is odd or false if even
678 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
679
680 return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't
681 ($x->{_d}->is_one() && $x->{_n}->is_odd()); # x/2 is not, but 3/1
682 0;
683 }
684
685sub is_even
686 {
687 # return true if arg (BINT or num_str) is even or false if odd
688 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
689
690 return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't
691 return 1 if ($x->{_d}->is_one() # x/3 is never
692 && $x->{_n}->is_even()); # but 4/1 is
693 0;
694 }
695
696BEGIN
697 {
698 *objectify = \&Math::BigInt::objectify;
699 }
700
701##############################################################################
702# parts() and friends
703
704sub numerator
705 {
706 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
a4e2b1c6
JH
707
708 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
709
184f15d5
JH
710 my $n = $x->{_n}->copy(); $n->{sign} = $x->{sign};
711 $n;
712 }
713
714sub denominator
715 {
716 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
717
a4e2b1c6 718 return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
184f15d5
JH
719 $x->{_d}->copy();
720 }
721
722sub parts
723 {
724 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
725
a4e2b1c6
JH
726 return ($self->bnan(),$self->bnan()) if $x->{sign} eq 'NaN';
727 return ($self->binf(),$self->binf()) if $x->{sign} eq '+inf';
728 return ($self->binf('-'),$self->binf()) if $x->{sign} eq '-inf';
729
184f15d5
JH
730 my $n = $x->{_n}->copy();
731 $n->{sign} = $x->{sign};
a4e2b1c6 732 return ($n,$x->{_d}->copy());
184f15d5
JH
733 }
734
735sub length
736 {
737 return 0;
738 }
739
740sub digit
741 {
742 return 0;
743 }
744
745##############################################################################
746# special calc routines
747
748sub bceil
749 {
750 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
751
752 return $x unless $x->{sign} =~ /^[+-]$/;
753 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
754
990fb837
RGS
755 local $Math::BigInt::upgrade = undef;
756 local $Math::BigInt::accuracy = undef;
757 local $Math::BigInt::precision = undef;
a4e2b1c6 758 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
184f15d5
JH
759 $x->{_d}->bone();
760 $x->{_n}->binc() if $x->{sign} eq '+'; # +22/7 => 4/1
a4e2b1c6 761 $x->{sign} = '+' if $x->{_n}->is_zero(); # -0 => 0
184f15d5
JH
762 $x;
763 }
764
765sub bfloor
766 {
767 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
768
769 return $x unless $x->{sign} =~ /^[+-]$/;
770 return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
771
990fb837
RGS
772 local $Math::BigInt::upgrade = undef;
773 local $Math::BigInt::accuracy = undef;
774 local $Math::BigInt::precision = undef;
a4e2b1c6 775 $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
184f15d5
JH
776 $x->{_d}->bone();
777 $x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
778 $x;
779 }
780
781sub bfac
782 {
a4e2b1c6
JH
783 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
784
7afd7a91 785 # if $x is an integer
a4e2b1c6
JH
786 if (($x->{sign} eq '+') && ($x->{_d}->is_one()))
787 {
788 $x->{_n}->bfac();
789 return $x->round(@r);
790 }
791 $x->bnan();
184f15d5
JH
792 }
793
794sub bpow
795 {
7d341013
T
796 # power ($x ** $y)
797
798 # set up parameters
799 my ($self,$x,$y,@r) = (ref($_[0]),@_);
800 # objectify is costly, so avoid it
801 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
802 {
803 ($self,$x,$y,@r) = objectify(2,@_);
804 }
184f15d5
JH
805
806 return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x
807 return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
808 return $x->bone(@r) if $y->is_zero();
809 return $x->round(@r) if $x->is_one() || $y->is_one();
810 if ($x->{sign} eq '-' && $x->{_n}->is_one() && $x->{_d}->is_one())
811 {
812 # if $x == -1 and odd/even y => +1/-1
813 return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r);
814 # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1;
815 }
816 # 1 ** -y => 1 / (1 ** |y|)
817 # so do test for negative $y after above's clause
818 # return $x->bnan() if $y->{sign} eq '-';
819 return $x->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0)
820
a4e2b1c6
JH
821 # shortcut y/1 (and/or x/1)
822 if ($y->{_d}->is_one())
823 {
824 # shortcut for x/1 and y/1
825 if ($x->{_d}->is_one())
826 {
827 $x->{_n}->bpow($y->{_n}); # x/1 ** y/1 => (x ** y)/1
828 if ($y->{sign} eq '-')
829 {
830 # 0.2 ** -3 => 1/(0.2 ** 3)
831 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
832 }
833 # correct sign; + ** + => +
834 if ($x->{sign} eq '-')
835 {
836 # - * - => +, - * - * - => -
837 $x->{sign} = '+' if $y->{_n}->is_even();
838 }
839 return $x->round(@r);
840 }
841 # x/z ** y/1
842 $x->{_n}->bpow($y->{_n}); # 5/2 ** y/1 => 5 ** y / 2 ** y
843 $x->{_d}->bpow($y->{_n});
844 if ($y->{sign} eq '-')
845 {
846 # 0.2 ** -3 => 1/(0.2 ** 3)
847 ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
848 }
849 # correct sign; + ** + => +
850 if ($x->{sign} eq '-')
851 {
852 # - * - => +, - * - * - => -
853 $x->{sign} = '+' if $y->{_n}->is_even();
854 }
855 return $x->round(@r);
856 }
857
858 # regular calculation (this is wrong for d/e ** f/g)
184f15d5 859 my $pow2 = $self->__one();
a4e2b1c6
JH
860 my $y1 = $MBI->new($y->{_n}/$y->{_d})->babs();
861 my $two = $MBI->new(2);
184f15d5
JH
862 while (!$y1->is_one())
863 {
184f15d5
JH
864 $pow2->bmul($x) if $y1->is_odd();
865 $y1->bdiv($two);
866 $x->bmul($x);
867 }
868 $x->bmul($pow2) unless $pow2->is_one();
869 # n ** -x => 1/n ** x
870 ($x->{_d},$x->{_n}) = ($x->{_n},$x->{_d}) if $y->{sign} eq '-';
7d341013 871 $x->bnorm()->round(@r);
184f15d5
JH
872 }
873
874sub blog
875 {
7afd7a91
T
876 # set up parameters
877 my ($self,$x,$y,@r) = (ref($_[0]),@_);
878
879 # objectify is costly, so avoid it
880 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
881 {
882 ($self,$x,$y,@r) = objectify(2,@_);
883 }
884
885 # $x <= 0 => NaN
886 return $x->bnan() if $x->is_zero() || $x->{sign} ne '+' || $y->{sign} ne '+';
887
888 if ($x->is_int() && $y->is_int())
889 {
890 return $self->new($x->as_number()->blog($y->as_number(),@r));
891 }
892
893 warn ("blog() not fully implemented");
894 $x->bnan();
895 }
896
897sub broot
898 {
899 # set up parameters
900 my ($self,$x,$y,@r) = (ref($_[0]),@_);
901 # objectify is costly, so avoid it
902 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
903 {
904 ($self,$x,$y,@r) = objectify(2,@_);
905 }
906
907 if ($x->is_int() && $y->is_int())
908 {
909 return $self->new($x->as_number()->broot($y->as_number(),@r));
910 }
911
912 warn ("broot() not fully implemented");
913 $x->bnan();
914 }
915
916sub bmodpow
917 {
918 # set up parameters
919 my ($self,$x,$y,$m,@r) = (ref($_[0]),@_);
920 # objectify is costly, so avoid it
921 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
922 {
923 ($self,$x,$y,$m,@r) = objectify(3,@_);
924 }
925
926 # $x or $y or $m are NaN or +-inf => NaN
927 return $x->bnan()
928 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/ ||
929 $m->{sign} !~ /^[+-]$/;
930
931 if ($x->is_int() && $y->is_int() && $m->is_int())
932 {
933 return $self->new($x->as_number()->bmodpow($y->as_number(),$m,@r));
934 }
935
936 warn ("bmodpow() not fully implemented");
937 $x->bnan();
938 }
939
940sub bmodinv
941 {
942 # set up parameters
943 my ($self,$x,$y,@r) = (ref($_[0]),@_);
944 # objectify is costly, so avoid it
945 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
946 {
947 ($self,$x,$y,@r) = objectify(2,@_);
948 }
949
950 # $x or $y are NaN or +-inf => NaN
951 return $x->bnan()
952 if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/;
953
954 if ($x->is_int() && $y->is_int())
955 {
956 return $self->new($x->as_number()->bmodinv($y->as_number(),@r));
957 }
958
959 warn ("bmodinv() not fully implemented");
960 $x->bnan();
184f15d5
JH
961 }
962
963sub bsqrt
964 {
990fb837
RGS
965 my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
966
967 return $x->bnan() if $x->{sign} !~ /^[+]/; # NaN, -inf or < 0
968 return $x if $x->{sign} eq '+inf'; # sqrt(inf) == inf
969 return $x->round(@r) if $x->is_zero() || $x->is_one();
970
971 local $Math::BigFloat::upgrade = undef;
972 local $Math::BigFloat::downgrade = undef;
973 local $Math::BigFloat::precision = undef;
974 local $Math::BigFloat::accuracy = undef;
975 local $Math::BigInt::upgrade = undef;
976 local $Math::BigInt::precision = undef;
977 local $Math::BigInt::accuracy = undef;
93c87d9d
T
978 $x->{_d} = Math::BigFloat->new($x->{_d})->bsqrt();
979 $x->{_n} = Math::BigFloat->new($x->{_n})->bsqrt();
184f15d5 980
990fb837
RGS
981 # if sqrt(D) was not integer
982 if ($x->{_d}->{_e}->{sign} ne '+')
983 {
984 $x->{_n}->blsft($x->{_d}->{_e}->babs(),10); # 7.1/4.51 => 7.1/45.1
985 $x->{_d} = $x->{_d}->{_m}; # 7.1/45.1 => 71/45.1
986 }
987 # if sqrt(N) was not integer
988 if ($x->{_n}->{_e}->{sign} ne '+')
989 {
990 $x->{_d}->blsft($x->{_n}->{_e}->babs(),10); # 71/45.1 => 710/45.1
93c87d9d 991 $x->{_n} = $x->{_n}->{_m}; # 710/45.1 => 710/451
990fb837
RGS
992 }
993
994 # convert parts to $MBI again
995 $x->{_n} = $x->{_n}->as_number();
996 $x->{_d} = $x->{_d}->as_number();
997 $x->bnorm()->round(@r);
184f15d5
JH
998 }
999
1000sub blsft
1001 {
1002 my ($self,$x,$y,$b,$a,$p,$r) = objectify(3,@_);
1003
1004 $x->bmul( $b->copy()->bpow($y), $a,$p,$r);
1005 $x;
1006 }
1007
1008sub brsft
1009 {
1010 my ($self,$x,$y,$b,$a,$p,$r) = objectify(2,@_);
1011
1012 $x->bdiv( $b->copy()->bpow($y), $a,$p,$r);
1013 $x;
1014 }
1015
1016##############################################################################
1017# round
1018
1019sub round
1020 {
1021 $_[0];
1022 }
1023
1024sub bround
1025 {
1026 $_[0];
1027 }
1028
1029sub bfround
1030 {
1031 $_[0];
1032 }
1033
1034##############################################################################
1035# comparing
1036
1037sub bcmp
1038 {
7afd7a91
T
1039 # compare two signed numbers
1040
1041 # set up parameters
1042 my ($self,$x,$y) = (ref($_[0]),@_);
1043 # objectify is costly, so avoid it
1044 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
1045 {
1046 ($self,$x,$y) = objectify(2,@_);
1047 }
184f15d5
JH
1048
1049 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
1050 {
1051 # handle +-inf and NaN
1052 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
1053 return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/;
1054 return +1 if $x->{sign} eq '+inf';
1055 return -1 if $x->{sign} eq '-inf';
1056 return -1 if $y->{sign} eq '+inf';
1057 return +1;
1058 }
1059 # check sign for speed first
1060 return 1 if $x->{sign} eq '+' && $y->{sign} eq '-'; # does also 0 <=> -y
1061 return -1 if $x->{sign} eq '-' && $y->{sign} eq '+'; # does also -x <=> 0
1062
1063 # shortcut
1064 my $xz = $x->{_n}->is_zero();
1065 my $yz = $y->{_n}->is_zero();
1066 return 0 if $xz && $yz; # 0 <=> 0
1067 return -1 if $xz && $y->{sign} eq '+'; # 0 <=> +y
1068 return 1 if $yz && $x->{sign} eq '+'; # +x <=> 0
1069
1070 my $t = $x->{_n} * $y->{_d}; $t->{sign} = $x->{sign};
1071 my $u = $y->{_n} * $x->{_d}; $u->{sign} = $y->{sign};
1072 $t->bcmp($u);
1073 }
1074
1075sub bacmp
1076 {
7afd7a91
T
1077 # compare two numbers (as unsigned)
1078
1079 # set up parameters
1080 my ($self,$x,$y) = (ref($_[0]),@_);
1081 # objectify is costly, so avoid it
1082 if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
1083 {
1084 ($self,$x,$y) = objectify(2,@_);
1085 }
184f15d5
JH
1086
1087 if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
1088 {
1089 # handle +-inf and NaN
1090 return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
1091 return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/;
7afd7a91
T
1092 return 1 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} !~ /^[+-]inf$/;
1093 return -1;
184f15d5
JH
1094 }
1095
1096 my $t = $x->{_n} * $y->{_d};
1097 my $u = $y->{_n} * $x->{_d};
1098 $t->bacmp($u);
1099 }
1100
1101##############################################################################
1102# output conversation
1103
7d341013
T
1104sub numify
1105 {
1106 # convert 17/8 => float (aka 2.125)
1107 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
1108
1109 return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, NaN, etc
1110
93c87d9d
T
1111 # N/1 => N
1112 return $x->{_n}->numify() if $x->{_d}->is_one();
1113
1114 # N/D
1115 my $neg = 1; $neg = -1 if $x->{sign} ne '+';
1116 $neg * $x->{_n}->numify() / $x->{_d}->numify(); # return sign * N/D
7d341013
T
1117 }
1118
184f15d5
JH
1119sub as_number
1120 {
1121 my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
1122
990fb837
RGS
1123 return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf etc
1124
1125 # need to disable these, otherwise bdiv() gives BigRat again
1126 local $Math::BigInt::upgrade = undef;
1127 local $Math::BigInt::accuracy = undef;
1128 local $Math::BigInt::precision = undef;
184f15d5
JH
1129 my $t = $x->{_n}->copy()->bdiv($x->{_d}); # 22/7 => 3
1130 $t->{sign} = $x->{sign};
1131 $t;
1132 }
1133
6de7f0cc
JH
1134sub import
1135 {
1136 my $self = shift;
1137 my $l = scalar @_;
1138 my $lib = ''; my @a;
1139 for ( my $i = 0; $i < $l ; $i++)
1140 {
1141# print "at $_[$i] (",$_[$i+1]||'undef',")\n";
1142 if ( $_[$i] eq ':constant' )
1143 {
1144 # this rest causes overlord er load to step in
1145 # print "overload @_\n";
1146 overload::constant float => sub { $self->new(shift); };
1147 }
1148# elsif ($_[$i] eq 'upgrade')
1149# {
1150# # this causes upgrading
1151# $upgrade = $_[$i+1]; # or undef to disable
1152# $i++;
1153# }
1154 elsif ($_[$i] eq 'downgrade')
1155 {
1156 # this causes downgrading
1157 $downgrade = $_[$i+1]; # or undef to disable
1158 $i++;
1159 }
1160 elsif ($_[$i] eq 'lib')
1161 {
1162 $lib = $_[$i+1] || ''; # default Calc
1163 $i++;
1164 }
1165 elsif ($_[$i] eq 'with')
1166 {
1167 $MBI = $_[$i+1] || 'Math::BigInt'; # default Math::BigInt
1168 $i++;
1169 }
1170 else
1171 {
1172 push @a, $_[$i];
1173 }
1174 }
1175 # let use Math::BigInt lib => 'GMP'; use Math::BigFloat; still work
1176 my $mbilib = eval { Math::BigInt->config()->{lib} };
1177 if ((defined $mbilib) && ($MBI eq 'Math::BigInt'))
1178 {
1179 # MBI already loaded
1180 $MBI->import('lib',"$lib,$mbilib", 'objectify');
1181 }
1182 else
1183 {
a4e2b1c6 1184 # MBI not loaded, or not with "Math::BigInt"
6de7f0cc
JH
1185 $lib .= ",$mbilib" if defined $mbilib;
1186
6de7f0cc
JH
1187 if ($] < 5.006)
1188 {
1189 # Perl < 5.6.0 dies with "out of memory!" when eval() and ':constant' is
1190 # used in the same script, or eval inside import().
1191 my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt
1192 my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm
1193 $file = File::Spec->catfile (@parts, $file);
1194 eval { require $file; $MBI->import( lib => '$lib', 'objectify' ); }
1195 }
1196 else
1197 {
1198 my $rc = "use $MBI lib => '$lib', 'objectify';";
1199 eval $rc;
1200 }
1201 }
93c87d9d
T
1202 if ($@)
1203 {
1204 require Carp; Carp::croak ("Couldn't load $MBI: $! $@");
1205 }
6de7f0cc
JH
1206
1207 # any non :constant stuff is handled by our parent, Exporter
1208 # even if @_ is empty, to give it a chance
1209 $self->SUPER::import(@a); # for subclasses
1210 $self->export_to_level(1,$self,@a); # need this, too
1211 }
184f15d5
JH
1212
12131;
1214
1215__END__
1216
1217=head1 NAME
1218
7afd7a91 1219Math::BigRat - arbitrarily big rational numbers
184f15d5
JH
1220
1221=head1 SYNOPSIS
1222
7d341013 1223 use Math::BigRat;
184f15d5 1224
7afd7a91 1225 my $x = Math::BigRat->new('3/7'); $x += '5/9';
184f15d5 1226
7d341013
T
1227 print $x->bstr(),"\n";
1228 print $x ** 2,"\n";
184f15d5 1229
7afd7a91
T
1230 my $y = Math::BigRat->new('inf');
1231 print "$y ", ($y->is_inf ? 'is' : 'is not') , " infinity\n";
1232
1233 my $z = Math::BigRat->new(144); $z->bsqrt();
1234
184f15d5
JH
1235=head1 DESCRIPTION
1236
7d341013 1237Math::BigRat complements Math::BigInt and Math::BigFloat by providing support
7afd7a91 1238for arbitrarily big rational numbers.
184f15d5
JH
1239
1240=head2 MATH LIBRARY
1241
1242Math with the numbers is done (by default) by a module called
1243Math::BigInt::Calc. This is equivalent to saying:
1244
1245 use Math::BigRat lib => 'Calc';
1246
1247You can change this by using:
1248
1249 use Math::BigRat lib => 'BitVect';
1250
1251The following would first try to find Math::BigInt::Foo, then
1252Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc:
1253
1254 use Math::BigRat lib => 'Foo,Math::BigInt::Bar';
1255
1256Calc.pm uses as internal format an array of elements of some decimal base
7d341013 1257(usually 1e7, but this might be different for some systems) with the least
184f15d5
JH
1258significant digit first, while BitVect.pm uses a bit vector of base 2, most
1259significant bit first. Other modules might use even different means of
1260representing the numbers. See the respective module documentation for further
1261details.
1262
7d341013
T
1263Currently the following replacement libraries exist, search for them at CPAN:
1264
1265 Math::BigInt::BitVect
1266 Math::BigInt::GMP
1267 Math::BigInt::Pari
1268 Math::BigInt::FastCalc
1269
184f15d5
JH
1270=head1 METHODS
1271
7d341013 1272Any methods not listed here are dervied from Math::BigFloat (or
6de7f0cc
JH
1273Math::BigInt), so make sure you check these two modules for further
1274information.
1275
1276=head2 new()
184f15d5
JH
1277
1278 $x = Math::BigRat->new('1/3');
1279
1280Create a new Math::BigRat object. Input can come in various forms:
1281
7d341013 1282 $x = Math::BigRat->new(123); # scalars
7afd7a91 1283 $x = Math::BigRat->new('inf'); # infinity
7d341013 1284 $x = Math::BigRat->new('123.3'); # float
184f15d5
JH
1285 $x = Math::BigRat->new('1/3'); # simple string
1286 $x = Math::BigRat->new('1 / 3'); # spaced
1287 $x = Math::BigRat->new('1 / 0.1'); # w/ floats
1288 $x = Math::BigRat->new(Math::BigInt->new(3)); # BigInt
1289 $x = Math::BigRat->new(Math::BigFloat->new('3.1')); # BigFloat
6de7f0cc 1290 $x = Math::BigRat->new(Math::BigInt::Lite->new('2')); # BigLite
184f15d5 1291
6de7f0cc 1292=head2 numerator()
184f15d5
JH
1293
1294 $n = $x->numerator();
1295
1296Returns a copy of the numerator (the part above the line) as signed BigInt.
1297
6de7f0cc 1298=head2 denominator()
184f15d5
JH
1299
1300 $d = $x->denominator();
1301
1302Returns a copy of the denominator (the part under the line) as positive BigInt.
1303
6de7f0cc 1304=head2 parts()
184f15d5
JH
1305
1306 ($n,$d) = $x->parts();
1307
1308Return a list consisting of (signed) numerator and (unsigned) denominator as
1309BigInts.
1310
6de7f0cc
JH
1311=head2 as_number()
1312
7d341013
T
1313 $x = Math::BigRat->new('13/7');
1314 print $x->as_number(),"\n"; # '1'
1315
990fb837 1316Returns a copy of the object as BigInt trunced it to integer.
6de7f0cc 1317
a4e2b1c6 1318=head2 bfac()
6de7f0cc 1319
a4e2b1c6 1320 $x->bfac();
6de7f0cc 1321
a4e2b1c6 1322Calculates the factorial of $x. For instance:
6de7f0cc 1323
a4e2b1c6
JH
1324 print Math::BigRat->new('3/1')->bfac(),"\n"; # 1*2*3
1325 print Math::BigRat->new('5/1')->bfac(),"\n"; # 1*2*3*4*5
184f15d5 1326
7d341013 1327Works currently only for integers.
6de7f0cc 1328
a4e2b1c6 1329=head2 blog()
6de7f0cc 1330
a4e2b1c6 1331Is not yet implemented.
6de7f0cc 1332
a4e2b1c6 1333=head2 bround()/round()/bfround()
6de7f0cc 1334
a4e2b1c6 1335Are not yet implemented.
6de7f0cc 1336
990fb837
RGS
1337=head2 bmod()
1338
1339 use Math::BigRat;
1340 my $x = Math::BigRat->new('7/4');
1341 my $y = Math::BigRat->new('4/3');
1342 print $x->bmod($y);
1343
1344Set $x to the remainder of the division of $x by $y.
1345
7d341013
T
1346=head2 is_one()
1347
1348 print "$x is 1\n" if $x->is_one();
1349
1350Return true if $x is exactly one, otherwise false.
1351
1352=head2 is_zero()
1353
1354 print "$x is 0\n" if $x->is_zero();
1355
1356Return true if $x is exactly zero, otherwise false.
1357
1358=head2 is_positive()
1359
1360 print "$x is >= 0\n" if $x->is_positive();
1361
1362Return true if $x is positive (greater than or equal to zero), otherwise
1363false. Please note that '+inf' is also positive, while 'NaN' and '-inf' aren't.
1364
1365=head2 is_negative()
1366
1367 print "$x is < 0\n" if $x->is_negative();
1368
1369Return true if $x is negative (smaller than zero), otherwise false. Please
1370note that '-inf' is also negative, while 'NaN' and '+inf' aren't.
1371
1372=head2 is_int()
1373
1374 print "$x is an integer\n" if $x->is_int();
1375
1376Return true if $x has a denominator of 1 (e.g. no fraction parts), otherwise
1377false. Please note that '-inf', 'inf' and 'NaN' aren't integer.
1378
1379=head2 is_odd()
1380
1381 print "$x is odd\n" if $x->is_odd();
1382
1383Return true if $x is odd, otherwise false.
1384
1385=head2 is_even()
1386
1387 print "$x is even\n" if $x->is_even();
1388
1389Return true if $x is even, otherwise false.
1390
1391=head2 bceil()
1392
1393 $x->bceil();
1394
1395Set $x to the next bigger integer value (e.g. truncate the number to integer
1396and then increment it by one).
1397
1398=head2 bfloor()
1399
1400 $x->bfloor();
1401
1402Truncate $x to an integer value.
6de7f0cc 1403
7afd7a91
T
1404=head2 bsqrt()
1405
1406 $x->bsqrt();
1407
1408Calculate the square root of $x.
1409
990fb837
RGS
1410=head2 config
1411
1412 use Data::Dumper;
1413
1414 print Dumper ( Math::BigRat->config() );
1415 print Math::BigRat->config()->{lib},"\n";
1416
1417Returns a hash containing the configuration, e.g. the version number, lib
1418loaded etc. The following hash keys are currently filled in with the
1419appropriate information.
1420
1421 key RO/RW Description
1422 Example
1423 ============================================================
1424 lib RO Name of the Math library
1425 Math::BigInt::Calc
1426 lib_version RO Version of 'lib'
1427 0.30
1428 class RO The class of config you just called
1429 Math::BigRat
1430 version RO version number of the class you used
1431 0.10
1432 upgrade RW To which class numbers are upgraded
1433 undef
1434 downgrade RW To which class numbers are downgraded
1435 undef
1436 precision RW Global precision
1437 undef
1438 accuracy RW Global accuracy
1439 undef
1440 round_mode RW Global round mode
1441 even
1442 div_scale RW Fallback acccuracy for div
1443 40
1444 trap_nan RW Trap creation of NaN (undef = no)
1445 undef
1446 trap_inf RW Trap creation of +inf/-inf (undef = no)
1447 undef
1448
1449By passing a reference to a hash you may set the configuration values. This
1450works only for values that a marked with a C<RW> above, anything else is
1451read-only.
1452
a4e2b1c6 1453=head1 BUGS
6de7f0cc 1454
7d341013
T
1455Some things are not yet implemented, or only implemented half-way:
1456
1457=over 2
1458
1459=item inf handling (partial)
1460
1461=item NaN handling (partial)
1462
1463=item rounding (not implemented except for bceil/bfloor)
1464
1465=item $x ** $y where $y is not an integer
1466
7afd7a91
T
1467=item bmod(), blog(), bmodinv() and bmodpow() (partial)
1468
7d341013 1469=back
184f15d5
JH
1470
1471=head1 LICENSE
1472
1473This program is free software; you may redistribute it and/or modify it under
1474the same terms as Perl itself.
1475
1476=head1 SEE ALSO
1477
1478L<Math::BigFloat> and L<Math::Big> as well as L<Math::BigInt::BitVect>,
1479L<Math::BigInt::Pari> and L<Math::BigInt::GMP>.
1480
7d341013
T
1481See L<http://search.cpan.org/search?dist=bignum> for a way to use
1482Math::BigRat.
1483
1484The package at L<http://search.cpan.org/search?dist=Math%3A%3ABigRat>
1485may contain more documentation and examples as well as testcases.
184f15d5
JH
1486
1487=head1 AUTHORS
1488
7afd7a91 1489(C) by Tels L<http://bloodgate.com/> 2001, 2002, 2003, 2004.
184f15d5
JH
1490
1491=cut