This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
untodo the no-longer-failing todo test for rgs' patch
[perl5.git] / lib / bigrat.pl
1 package bigrat;
2 require "bigint.pl";
3 #
4 # This library is no longer being maintained, and is included for backward
5 # compatibility with Perl 4 programs which may require it.
6 # This legacy library is deprecated and will be removed in a future
7 # release of perl.
8 #
9 # In particular, this should not be used as an example of modern Perl
10 # programming techniques.
11 #
12 # Arbitrary size rational math package
13
14 warn( "The 'bigrat.pl' legacy library is deprecated and will be"
15       . " removed in the next major release of perl. Please use the"
16       . " bigrat module instead." );
17
18 # by Mark Biggar
19 #
20 # Input values to these routines consist of strings of the form 
21 #   m|^\s*[+-]?[\d\s]+(/[\d\s]+)?$|.
22 # Examples:
23 #   "+0/1"                          canonical zero value
24 #   "3"                             canonical value "+3/1"
25 #   "   -123/123 123"               canonical value "-1/1001"
26 #   "123 456/7890"                  canonical value "+20576/1315"
27 # Output values always include a sign and no leading zeros or
28 #   white space.
29 # This package makes use of the bigint package.
30 # The string 'NaN' is used to represent the result when input arguments 
31 #   that are not numbers, as well as the result of dividing by zero and
32 #       the sqrt of a negative number.
33 # Extreamly naive algorthims are used.
34 #
35 # Routines provided are:
36 #
37 #   rneg(RAT) return RAT                negation
38 #   rabs(RAT) return RAT                absolute value
39 #   rcmp(RAT,RAT) return CODE           compare numbers (undef,<0,=0,>0)
40 #   radd(RAT,RAT) return RAT            addition
41 #   rsub(RAT,RAT) return RAT            subtraction
42 #   rmul(RAT,RAT) return RAT            multiplication
43 #   rdiv(RAT,RAT) return RAT            division
44 #   rmod(RAT) return (RAT,RAT)          integer and fractional parts
45 #   rnorm(RAT) return RAT               normalization
46 #   rsqrt(RAT, cycles) return RAT       square root
47 \f
48 # Convert a number to the canonical string form m|^[+-]\d+/\d+|.
49 sub main'rnorm { #(string) return rat_num
50     local($_) = @_;
51     s/\s+//g;
52     if (m#^([+-]?\d+)(/(\d*[1-9]0*))?$#) {
53         &norm($1, $3 ? $3 : '+1');
54     } else {
55         'NaN';
56     }
57 }
58
59 # Normalize by reducing to lowest terms
60 sub norm { #(bint, bint) return rat_num
61     local($num,$dom) = @_;
62     if ($num eq 'NaN') {
63         'NaN';
64     } elsif ($dom eq 'NaN') {
65         'NaN';
66     } elsif ($dom =~ /^[+-]?0+$/) {
67         'NaN';
68     } else {
69         local($gcd) = &'bgcd($num,$dom);
70         $gcd =~ s/^-/+/;
71         if ($gcd ne '+1') { 
72             $num = &'bdiv($num,$gcd);
73             $dom = &'bdiv($dom,$gcd);
74         } else {
75             $num = &'bnorm($num);
76             $dom = &'bnorm($dom);
77         }
78         substr($dom,$[,1) = '';
79         "$num/$dom";
80     }
81 }
82
83 # negation
84 sub main'rneg { #(rat_num) return rat_num
85     local($_) = &'rnorm(@_);
86     tr/-+/+-/ if ($_ ne '+0/1');
87     $_;
88 }
89
90 # absolute value
91 sub main'rabs { #(rat_num) return $rat_num
92     local($_) = &'rnorm(@_);
93     substr($_,$[,1) = '+' unless $_ eq 'NaN';
94     $_;
95 }
96
97 # multipication
98 sub main'rmul { #(rat_num, rat_num) return rat_num
99     local($xn,$xd) = split('/',&'rnorm($_[$[]));
100     local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
101     &norm(&'bmul($xn,$yn),&'bmul($xd,$yd));
102 }
103
104 # division
105 sub main'rdiv { #(rat_num, rat_num) return rat_num
106     local($xn,$xd) = split('/',&'rnorm($_[$[]));
107     local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
108     &norm(&'bmul($xn,$yd),&'bmul($xd,$yn));
109 }
110 \f
111 # addition
112 sub main'radd { #(rat_num, rat_num) return rat_num
113     local($xn,$xd) = split('/',&'rnorm($_[$[]));
114     local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
115     &norm(&'badd(&'bmul($xn,$yd),&'bmul($yn,$xd)),&'bmul($xd,$yd));
116 }
117
118 # subtraction
119 sub main'rsub { #(rat_num, rat_num) return rat_num
120     local($xn,$xd) = split('/',&'rnorm($_[$[]));
121     local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
122     &norm(&'bsub(&'bmul($xn,$yd),&'bmul($yn,$xd)),&'bmul($xd,$yd));
123 }
124
125 # comparison
126 sub main'rcmp { #(rat_num, rat_num) return cond_code
127     local($xn,$xd) = split('/',&'rnorm($_[$[]));
128     local($yn,$yd) = split('/',&'rnorm($_[$[+1]));
129     &bigint'cmp(&'bmul($xn,$yd),&'bmul($yn,$xd));
130 }
131
132 # int and frac parts
133 sub main'rmod { #(rat_num) return (rat_num,rat_num)
134     local($xn,$xd) = split('/',&'rnorm(@_));
135     local($i,$f) = &'bdiv($xn,$xd);
136     if (wantarray) {
137         ("$i/1", "$f/$xd");
138     } else {
139         "$i/1";
140     }   
141 }
142
143 # square root by Newtons method.
144 #   cycles specifies the number of iterations default: 5
145 sub main'rsqrt { #(fnum_str[, cycles]) return fnum_str
146     local($x, $scale) = (&'rnorm($_[$[]), $_[$[+1]);
147     if ($x eq 'NaN') {
148         'NaN';
149     } elsif ($x =~ /^-/) {
150         'NaN';
151     } else {
152         local($gscale, $guess) = (0, '+1/1');
153         $scale = 5 if (!$scale);
154         while ($gscale++ < $scale) {
155             $guess = &'rmul(&'radd($guess,&'rdiv($x,$guess)),"+1/2");
156         }
157         "$guess";          # quotes necessary due to perl bug
158     }
159 }
160
161 1;