[system] / trunk / pg / lib / Fraction.pm Repository:
ViewVC logotype

Annotation of /trunk/pg/lib/Fraction.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1079 - (view) (download) (as text)

1 : apizer 1079
2 : sh002i 1050 #
3 :     # Fraction object
4 :     # Keeps track of two variables- numerator and denominator.
5 :     # Has subroutines for basic arithmatic functions, for anything
6 :     # more complicated, it can return a scalar value of
7 :     # numerator/denominator.
8 :     # VS 7/20/2000
9 :    
10 :    
11 :     =head3 Fraction
12 :    
13 :     This object is designed to ease the use of fractions
14 :    
15 :     =head4 Variables and Methods
16 :    
17 :     Variables
18 :    
19 :     numerator #numerator of fraction
20 :     denominator #denominator of fraction
21 :    
22 :     Arithmetic Methods #these will all accept a scalar value or
23 :     #another fraction as an argument
24 :    
25 :     plus #returns the sum of the fraction and argument
26 :     minus #returns fraction minus argument
27 :     subtractFrom #returns argument minus fraction
28 :     divBy #returns fraction divided by argument
29 :     divInto #returns argument divided by fraction
30 :     times #returns fraction times argument
31 :     compare #returns <, =, or > for the relation of fraction to argument
32 :    
33 :     pow #returns fraction raised to argument, a given integer power
34 :    
35 :    
36 :     Other methods
37 :    
38 :     reduce #reduces to lowest terms, and makes sure denominator is positive
39 :     scalar #returns the scalar value numerator/denominator
40 :     print #prints the fraction
41 :     print_mixed #prints the fractionas a mixed number
42 :     print_inline #prints the fraction like this 2/3
43 :    
44 :    
45 :     =head4 Synopsis
46 :    
47 :     The fraction object stores two variables, numerator and denominator. The basic
48 :     arithmatic methods listed above can be performed on a fraction, and it can return its own
49 :     scalar value for use with functions expecting a scalar (ie, sqrt($frac->scalar) ).
50 :    
51 :    
52 :     =cut
53 :    
54 :    
55 :     BEGIN {
56 :     be_strict();
57 :     }
58 :    
59 :     package Fraction;
60 :    
61 :    
62 :     my %fields = (
63 :     numerator => undef,
64 :     denominator => undef,
65 :     );
66 :    
67 :    
68 :     sub new {
69 :    
70 :     my $class = shift;
71 :     my @input = @_;
72 :     my $num;
73 :     my $denom;
74 :    
75 :     unless (@_ == 1 or @_ == 2) {
76 :     warn "Invalid number of arguments to create new Fraction. Use the form new Fraction(numerator,
77 :     denominator) or new Fraction(value) to send a single scalar.";
78 :     }
79 :    
80 :     # if we've been given a scalar as input:
81 :     # this will ensure that the numerator is a whole number. If it is not, this will
82 :     # multiply by 10 until it is a whole number, keeping track of the appropriate denominator.
83 :     # The loop conditional checks that the difference between the number and its int value
84 :     # is less than .000000001, NOT that they are equal. Because of imprecisions with floating
85 :     # point numbers, checking for equality will NOT work in many cases.
86 :    
87 :     if (@_ == 1) {
88 :     my $tempDenom = 1;
89 :     while ($input[0] - int($input[0]) > .000000001) {$input[0] *= 10; $tempDenom *= 10;}
90 :     $num = $input[0];
91 :     $denom = $tempDenom;
92 :     }
93 :    
94 :     else { $num = $input[0]; $denom = $input[1]; }
95 :    
96 :    
97 :     my $self = {
98 :     _permitted => \%fields,
99 :     numerator => $num,
100 :     denominator => $denom,
101 :     };
102 :    
103 :     bless $self, $class;
104 :    
105 :     return $self;
106 :     }
107 :    
108 :     sub AUTOLOAD {
109 :     my $self = shift;
110 :    
111 :     my $type = ref($self) or die "$self is not an object";
112 :    
113 :     # $AUTOLOAD is sent in by Perl and is the full name of the object (i.e. main::blah::blah_more)
114 :     my $name = $Fraction::AUTOLOAD;
115 :     $name =~ s/.*://; #strips fully-qualified portion
116 :    
117 :     unless ( exists $self->{'_permitted'}->{$name} ) { die "Can't find '$name' field in object of class '$type'";}
118 :    
119 :     if (@_) {
120 :     return $self->{$name} = shift; #set the variable to the first parameter
121 :     } else {
122 :     return $self->($name); #if no parameters just return the value
123 :     }
124 :     }
125 :    
126 :     sub DESTROY {
127 :     # doing nothing about destruction, hope that isn't dangerous
128 :     }
129 :    
130 :    
131 :     ###################################################################################
132 :     # Basic Arithmetic Methods
133 :     # Each returns a new Fraction appropriate to the operation
134 :    
135 :     sub plus {
136 :     my $self = shift;
137 :     my $input = shift;
138 :    
139 :     $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction");
140 :    
141 :     my $lcm = $self->lcm($self->{denominator}, $input->{denominator});
142 :     my $scaleA = $lcm/$self->{denominator};
143 :     my $scaleB = $lcm/$input->{denominator};
144 :    
145 :     my $num = $self->{numerator}*$scaleA + $input->{numerator}*$scaleB;
146 :    
147 :     my $frac = new Fraction($num, $lcm);
148 :     $frac->reduce;
149 :     $frac;
150 :     }
151 :    
152 :     sub minus {
153 :     my $self = shift;
154 :     my $input = shift;
155 :    
156 :     $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction");
157 :    
158 :     my $lcm = $self->lcm($self->{denominator}, $input->{denominator});
159 :     my $scaleA = $lcm/$self->{denominator};
160 :     my $scaleB = $lcm/$input->{denominator};
161 :    
162 :     my $num = $self->{numerator}*$scaleA - $input->{numerator}*$scaleB;
163 :    
164 :     my $frac = new Fraction($num, $lcm);
165 :     $frac->reduce;
166 :     $frac;
167 :     }
168 :    
169 :     sub subtractFrom {
170 :     my $self = shift;
171 :     my $input = shift;
172 :    
173 :     $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction");
174 :    
175 :     my $lcm = $self->lcm($self->{denominator}, $input->{denominator});
176 :     my $scaleA = $lcm/$self->{denominator};
177 :     my $scaleB = $lcm/$input->{denominator};
178 :    
179 :     my $num = $input->{numerator}*$scaleB - $self->{numerator}*$scaleA;
180 :    
181 :     my $frac = new Fraction($num, $lcm);
182 :     $frac->reduce;
183 :     $frac;
184 :     }
185 :    
186 :     sub divInto {
187 :     my $self = shift;
188 :     my $input = shift;
189 :    
190 :     $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction");
191 :    
192 :     my $num = $input->{numerator}*$self->{denominator};
193 :     my $denom = $input->{denominator}*$self->{numerator};
194 :    
195 :     my $frac = new Fraction($num, $denom);
196 :     $frac->reduce;
197 :     $frac;
198 :     }
199 :    
200 :     sub divBy {
201 :     my $self = shift;
202 :     my $input = shift;
203 :    
204 :     $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction");
205 :    
206 :     my $num = $self->{numerator}*$input->{denominator};
207 :     my $denom = $input->{numerator}*$self->{denominator};
208 :    
209 :     my $frac = new Fraction($num, $denom);
210 :     $frac->reduce;
211 :     $frac;
212 :     }
213 :    
214 :     sub times {
215 :     my $self = shift;
216 :     my $input = shift;
217 :    
218 :     $input = new Fraction($input*100, 100) unless (ref($input) eq "Fraction");
219 :    
220 :     my $num = $self->{numerator}*$input->{numerator};
221 :     my $denom = $self->{denominator}*$input->{denominator};
222 :    
223 :     my $frac = new Fraction($num, $denom);
224 :     $frac->reduce;
225 :     $frac;
226 :     }
227 :    
228 :     sub pow {
229 :    
230 :     my $self = shift;
231 :     my $input = shift;
232 :    
233 :     if($input == 0) { # 0 power, always return 1
234 :     if($self->{numerator} == 0) {
235 :     warn "Indeterminant form, 0^0, in Fraction power";
236 :     }
237 :     return new Fraction(1,0);
238 :     }
239 :    
240 :     my ($n, $d);
241 :    
242 :     if($input<0) {
243 :     $d = $self->{numerator};
244 :     $n = $self->{denominator};
245 :     if($d==0) {
246 :     warn "Computing 1/0 in Fraction";
247 :     }
248 :     $input = -$input;
249 :     } else {
250 :     $n = $self->{numerator};
251 :     $d = $self->{denominator};
252 :     }
253 :     my $g = $self->gcd($n, $d);
254 :     if($d<0) {$g = -$g;}
255 :     $n /= $g;
256 :     $d /= $g;
257 :    
258 :     return new Fraction($n**$input, $d**$input);
259 :    
260 :     }
261 :    
262 :     #########################################################################
263 :     # Other User-Accessed Methods
264 :    
265 :    
266 :     # returns a string denoting relation-- < = or >
267 :     # a string is returned for ease of use in writing problems
268 :     sub compare {
269 :     my $self = shift;
270 :     my $input = shift;
271 :    
272 :     $input = $input->scalar if (ref($input) eq "Fraction");
273 :    
274 :     my $relation = undef;
275 :     $relation = "<" if ($self->scalar < $input);
276 :     $relation = "=" if ($self->scalar == $input);
277 :     $relation = ">" if ($self->scalar > $input);
278 :    
279 :     $relation;
280 :     }
281 :    
282 :    
283 :     # returns the scalar value of numerator/denominator
284 :     sub scalar {
285 :     my $self = shift;
286 :     my $scalar = $self->{numerator}/$self->{denominator};
287 :    
288 :     $scalar;
289 :     }
290 :    
291 :    
292 :     # reduces a fraction to lowest terms, and makes denominator positive
293 :     sub reduce {
294 :    
295 :     my $self = shift;
296 :     my $gcd = $self->gcd($self->{numerator}, $self->{denominator});
297 :     if($self->{denominator}<0) {$gcd = -$gcd;}
298 :    
299 :     $self->{numerator} = $self->{numerator}/$gcd;
300 :     $self->{denominator} = $self->{denominator}/$gcd;
301 :     }
302 :    
303 :    
304 :     # standard print method. Outputs string containing fraction displayed (in math mode
305 :     # if needed).
306 :     sub print {
307 :     my $self = shift;
308 :     my $out;
309 :    
310 :     # if it's a whole number, just print the number
311 :     if ($self->{denominator} == 1) {
312 :     $out = $self->{numerator};
313 :     }
314 :     # positive fraction: print out in plain math mode
315 :     elsif ($self->scalar > 0) {
316 :     $out = "\\ensuremath{ \\frac{$self->{numerator}}{$self->{denominator}} }";
317 :     }
318 :     # negative fraction: print out negative sign and then absolute value in
319 :     # fraction form, avoiding parenthesis around the negative portion.
320 :     else {
321 :     my $foo = -$self->{numerator};
322 :     $out = "\\ensuremath{ -\\frac{$foo}{$self->{denominator}} }";
323 :     }
324 :    
325 :     $out;
326 :     }
327 :    
328 :     # forces printing of a mixed number, if applicable.
329 :     sub print_mixed {
330 :     my $self = shift;
331 :     my $out;
332 :    
333 :     # if it's not an improper, just pass on to the regular print method
334 :     if ($self->{numerator} < $self->{denominator} ) { $out = $self->print; }
335 :    
336 :     # otherwise print out the mixed number strong. This does not alter the
337 :     # actual value of the fraction in any way.
338 :     else {
339 :     my $tempNum = $self->{numerator};
340 :     my $tempDenom = $self->{denominator};
341 :     my $coeff = int($tempNum/$tempDenom);
342 :     $tempNum = $tempNum % $tempDenom;
343 :    
344 :     $out = "\\ensuremath{ -$coeff \\frac{abs($tempNum)}{abs($tempDenom)} }" if ($self->scalar < 0);
345 :     $out = "\\ensuremath{ $coeff \\frac{$tempNum}{$tempDenom} }" if ($self->scalar > 0);
346 :     $out = $coeff if ($tempNum == 0);
347 :     }
348 :    
349 :     $out;
350 :     }
351 :    
352 :    
353 :     # prints fraction as 4 or 5/3 as needed
354 :     sub print_inline {
355 :     my $self = shift;
356 :     my $out;
357 :    
358 :     # if it's a whole number, just print the number
359 :     if ($self->{denominator} == 1) {
360 :     $out = $self->{numerator};
361 :     }
362 :     # print as 5/3
363 :     else {
364 :     $out = "$self->{numerator}/$self->{denominator}";
365 :     }
366 :    
367 :     $out;
368 :     }
369 :    
370 :     # these methods are simply so that in a problem, the user may access the variables without
371 :     # worrying about braces, that is, use $frac->denominator instead of $frac->{denominator}
372 :     sub numerator {
373 :     my $self = shift;
374 :     return $self->{numerator};
375 :     }
376 :    
377 :     sub denominator {
378 :     my $self = shift;
379 :     return $self->{denominator};
380 :     }
381 :    
382 :     ########################################################################
383 :     # Internal Methods
384 :    
385 :     # Least Common Multiple
386 :     # Used in arithmatic methods to convert two fractions to common denominator
387 :     # takes in two scalar values and returns their lcm
388 :     sub lcm {
389 :     my $self = shift;
390 :     my $a = shift;
391 :     my $b = shift;
392 :    
393 :     #reorder such that $a is the smaller number
394 :     if ($a > $b) {
395 :     my $temp = $a;
396 :     $a = $b;
397 :     $b = $temp;
398 :     }
399 :    
400 :     my $lcm = 0;
401 :     my $curr = $b;
402 :    
403 :     while($lcm == 0) {
404 :     $lcm = $curr if ($curr % $a == 0);
405 :     $curr += $b;
406 :     }
407 :    
408 :     $lcm;
409 :     }
410 :    
411 :    
412 :    
413 :     # Helper function for reduce
414 :     # takes in two scalar values and uses the Euclidean Algorithm to return the
415 :     # greatest common denominator
416 :     sub gcd {
417 :    
418 :     my $self = shift;
419 :     my $a = abs(shift); #absolute values because this will yeild the same gcd,
420 :     my $b = abs(shift); #but allows use of the mod operation
421 :    
422 :     if ($a < $b) {
423 :     my $temp = $a;
424 :     $a = $b;
425 :     $b = $temp;
426 :     }
427 :    
428 :     return $a if $b == 0;
429 :    
430 :     my $q = int($a/$b);
431 :     my $r = $a % $b;
432 :    
433 :     return $b if $r == 0;
434 :    
435 :     my $tempR = $r;
436 :    
437 :     while ($r != 0) {
438 :    
439 :     #keep track of what $r was in the last loop, as this is the value
440 :     #we will want when $r is set to 0
441 :     $tempR = $r;
442 :    
443 :     $a = $b;
444 :     $b = $r;
445 :     $q = $a/$b;
446 :     $r = $a % $b;
447 :    
448 :     }
449 :    
450 :     $tempR;
451 :     }
452 :    
453 :    
454 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9