[system] / trunk / pg / lib / Fraction.pm Repository: Repository Listing bbplugincoursesdistsnplrochestersystemwww

# View of /trunk/pg/lib/Fraction.pm

Revision 1079 - (download) (as text) (annotate)
Mon Jun 9 17:36:12 2003 UTC (16 years, 8 months ago) by apizer
File size: 10823 byte(s)
```removed unneccesary shebang lines
```

```    1
2 #
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
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
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