File Coverage

blib/lib/bigrat.pl
Criterion Covered Total %
statement 0 54 0.0
branch 0 24 0.0
condition n/a
subroutine 0 11 0.0
pod 0 1 0.0
total 0 90 0.0


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