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

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

Revision 6346 - (download) (as text) (annotate)
Sat Jul 10 12:39:40 2010 UTC (9 years, 7 months ago) by gage
File size: 10840 byte(s)
```Merging changes gage branch  gage_dev/pg

removed dependence on AUTOLOAD	which does not work well with newer versions of Safe.pm.  It wasn't needed
in any case.  There remain other incompatibilies of WeBWorK with Safe.pm 2.27

Added more support for WARN_MESSAGE  and DEBUG_MESSAGE

Changed List.pm to ChoiceList.pm  to remove confusion with MathObjects List object

Additional support for geogebra applets

```

```    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
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 ##########################
109 # Access methods
110 ##########################
111 sub numerator {
112   my \$self = shift;
113   my \$type = ref(\$self) || die "\$self is not an object";
114   unless (exists \$self->{numerator} ) {
115     die "Can't find numerator field in object of class \$type";
116   }
117
118   if (@_) {
119     return \$self->{numerator} = shift;
120   } else {
121     return \$self->{numerator}
122   }
123 }
124
125 sub denominator {
126   my \$self = shift;
127   my \$type = ref(\$self) || die "\$self is not an object";
128   unless (exists \$self->{denominator} ) {
129     die "Can't find denominator field in object of class \$type";
130   }
131
132   if (@_) {
133     return \$self->{denominator} = shift;
134   } else {
135     return \$self->{denominator}
136   }
137 }
138
139 sub DESTROY {
140   # doing nothing about destruction, hope that isn't dangerous
141 }
142
143
144 ###################################################################################
145 # Basic Arithmetic Methods
146 # Each returns a new Fraction appropriate to the operation
147
148 sub plus {
149   my \$self = shift;
150   my \$input = shift;
151
152   \$input = new Fraction(\$input*100, 100) unless (ref(\$input) eq "Fraction");
153
154   my \$lcm = \$self->lcm(\$self->{denominator}, \$input->{denominator});
155   my \$scaleA = \$lcm/\$self->{denominator};
156   my \$scaleB = \$lcm/\$input->{denominator};
157
158   my \$num = \$self->{numerator}*\$scaleA + \$input->{numerator}*\$scaleB;
159
160   my \$frac = new Fraction(\$num, \$lcm);
161   \$frac->reduce;
162   \$frac;
163 }
164
165 sub minus {
166   my \$self = shift;
167   my \$input = shift;
168
169   \$input = new Fraction(\$input*100, 100) unless (ref(\$input) eq "Fraction");
170
171   my \$lcm = \$self->lcm(\$self->{denominator}, \$input->{denominator});
172   my \$scaleA = \$lcm/\$self->{denominator};
173   my \$scaleB = \$lcm/\$input->{denominator};
174
175   my \$num = \$self->{numerator}*\$scaleA - \$input->{numerator}*\$scaleB;
176
177   my \$frac = new Fraction(\$num, \$lcm);
178   \$frac->reduce;
179   \$frac;
180 }
181
182 sub subtractFrom {
183   my \$self = shift;
184   my \$input = shift;
185
186   \$input = new Fraction(\$input*100, 100) unless (ref(\$input) eq "Fraction");
187
188   my \$lcm = \$self->lcm(\$self->{denominator}, \$input->{denominator});
189   my \$scaleA = \$lcm/\$self->{denominator};
190   my \$scaleB = \$lcm/\$input->{denominator};
191
192   my \$num = \$input->{numerator}*\$scaleB - \$self->{numerator}*\$scaleA;
193
194   my \$frac = new Fraction(\$num, \$lcm);
195   \$frac->reduce;
196   \$frac;
197 }
198
199 sub divInto {
200   my \$self = shift;
201   my \$input = shift;
202
203   \$input = new Fraction(\$input*100, 100) unless (ref(\$input) eq "Fraction");
204
205   my \$num = \$input->{numerator}*\$self->{denominator};
206   my \$denom = \$input->{denominator}*\$self->{numerator};
207
208   my \$frac = new Fraction(\$num, \$denom);
209   \$frac->reduce;
210   \$frac;
211 }
212
213 sub divBy {
214   my \$self = shift;
215   my \$input = shift;
216
217   \$input = new Fraction(\$input*100, 100) unless (ref(\$input) eq "Fraction");
218
219   my \$num = \$self->{numerator}*\$input->{denominator};
220   my \$denom = \$input->{numerator}*\$self->{denominator};
221
222   my \$frac = new Fraction(\$num, \$denom);
223   \$frac->reduce;
224   \$frac;
225 }
226
227 sub times {
228   my \$self = shift;
229   my \$input = shift;
230
231   \$input = new Fraction(\$input*100, 100) unless (ref(\$input) eq "Fraction");
232
233   my \$num = \$self->{numerator}*\$input->{numerator};
234   my \$denom = \$self->{denominator}*\$input->{denominator};
235
236   my \$frac = new Fraction(\$num, \$denom);
237   \$frac->reduce;
238   \$frac;
239 }
240
241 sub pow {
242
243   my \$self = shift;
244   my \$input = shift;
245
246   if(\$input == 0) { # 0 power, always return 1
247     if(\$self->{numerator} == 0) {
248       warn "Indeterminant form, 0^0, in Fraction power";
249     }
250     return new Fraction(1,0);
251   }
252
253   my (\$n, \$d);
254
255   if(\$input<0) {
256     \$d = \$self->{numerator};
257     \$n = \$self->{denominator};
258     if(\$d==0) {
259       warn "Computing 1/0 in Fraction";
260     }
261     \$input = -\$input;
262   } else {
263     \$n = \$self->{numerator};
264     \$d = \$self->{denominator};
265   }
266   my \$g = \$self->gcd(\$n, \$d);
267   if(\$d<0) {\$g = -\$g;}
268   \$n /= \$g;
269   \$d /= \$g;
270
271   return new Fraction(\$n**\$input, \$d**\$input);
272
273 }
274
275 #########################################################################
276 # Other User-Accessed Methods
277
278
279 # returns a string denoting relation-- < = or >
280 # a string is returned for ease of use in writing problems
281 sub compare {
282   my \$self = shift;
283   my \$input = shift;
284
285   \$input = \$input->scalar if (ref(\$input) eq "Fraction");
286
287   my \$relation = undef;
288   \$relation = "<" if (\$self->scalar < \$input);
289   \$relation = "=" if (\$self->scalar == \$input);
290   \$relation = ">" if (\$self->scalar > \$input);
291
292   \$relation;
293 }
294
295
296 # returns the scalar value of numerator/denominator
297 sub scalar {
298   my \$self = shift;
299   my \$scalar = \$self->{numerator}/\$self->{denominator};
300
301   \$scalar;
302 }
303
304
305 # reduces a fraction to lowest terms, and makes denominator positive
306 sub reduce {
307
308   my \$self = shift;
309   my \$gcd = \$self->gcd(\$self->{numerator}, \$self->{denominator});
310   if(\$self->{denominator}<0) {\$gcd = -\$gcd;}
311
312   \$self->{numerator} = \$self->{numerator}/\$gcd;
313   \$self->{denominator} = \$self->{denominator}/\$gcd;
314 }
315
316
317 # standard print method.  Outputs string containing fraction displayed (in math mode
318 # if needed).
319 sub print {
320   my \$self = shift;
321   my \$out;
322
323   # if it's a whole number, just print the number
324   if (\$self->{denominator} == 1) {
325     \$out = \$self->{numerator};
326   }
327   # positive fraction: print out in plain math mode
328   elsif (\$self->scalar > 0) {
329     \$out = " \\frac{\$self->{numerator}}{\$self->{denominator}} ";
330   }
331   # negative fraction: print out negative sign and then absolute value in
332   # fraction form, avoiding parenthesis around the negative portion.
333   else {
334     my \$foo = -\$self->{numerator};
335     \$out = "  -\\frac{\$foo}{\$self->{denominator}} ";
336   }
337
338   \$out;
339 }
340
341 # forces printing of a mixed number, if applicable.
342 sub print_mixed {
343   my \$self = shift;
344   my \$out;
345
346   # if it's not an improper, just pass on to the regular print method
347   if (\$self->{numerator} < \$self->{denominator} ) { \$out = \$self->print; }
348
349   # otherwise print out the mixed number strong.  This does not alter the
350   # actual value of the fraction in any way.
351   else {
352     my \$tempNum = \$self->{numerator};
353     my \$tempDenom = \$self->{denominator};
354     my \$coeff = int(\$tempNum/\$tempDenom);
355     \$tempNum = \$tempNum % \$tempDenom;
356
357     \$out = " -\$coeff \\frac{abs(\$tempNum)}{abs(\$tempDenom)} " if (\$self->scalar < 0);
358     \$out = " \$coeff \\frac{\$tempNum}{\$tempDenom} " if (\$self->scalar > 0);
359     \$out = \$coeff if (\$tempNum == 0);
360   }
361
362   \$out;
363 }
364
365
366 # prints fraction as 4 or 5/3 as needed
367 sub print_inline {
368   my \$self = shift;
369   my \$out;
370
371   # if it's a whole number, just print the number
372   if (\$self->{denominator} == 1) {
373     \$out = \$self->{numerator};
374   }
375   # print as 5/3
376   else {
377     \$out = "\$self->{numerator}/\$self->{denominator}";
378   }
379
380   \$out;
381 }
382
383 # these methods are simply so that in a problem, the user may access the variables without
384 # worrying about braces, that is, use \$frac->denominator instead of \$frac->{denominator}
385 sub numerator {
386   my \$self = shift;
387   return \$self->{numerator};
388 }
389
390 sub denominator {
391   my \$self = shift;
392   return \$self->{denominator};
393 }
394
395 ########################################################################
396 # Internal Methods
397
398 # Least Common Multiple
399 # Used in arithmatic methods to convert two fractions to common denominator
400 # takes in two scalar values and returns their lcm
401 sub lcm {
402   my \$self = shift;
403         my \$a = shift;
404         my \$b = shift;
405
406         #reorder such that \$a is the smaller number
407         if (\$a > \$b) {
408                 my \$temp = \$a;
409                 \$a = \$b;
410                 \$b = \$temp;
411         }
412
413         my \$lcm = 0;
414         my \$curr = \$b;
415
416         while(\$lcm == 0) {
417                 \$lcm = \$curr if (\$curr % \$a == 0);
418                 \$curr += \$b;
419         }
420
421         \$lcm;
422 }
423
424
425
426 # Helper function for reduce
427 # takes in two scalar values and uses the Euclidean Algorithm to return the
428 # greatest common denominator
429 sub gcd {
430
431   my \$self = shift;
432         my \$a = abs(shift); #absolute values because this will yeild the same gcd,
433         my \$b = abs(shift); #but allows use of the mod operation
434
435   if (\$a < \$b) {
436     my \$temp = \$a;
437     \$a = \$b;
438     \$b = \$temp;
439   }
440
441   return \$a if \$b == 0;
442
443   my \$q = int(\$a/\$b);
444   my \$r = \$a % \$b;
445
446   return \$b if \$r == 0;
447
448   my \$tempR = \$r;
449
450   while (\$r != 0) {
451
452     #keep track of what \$r was in the last loop, as this is the value
453     #we will want when \$r is set to 0
454     \$tempR = \$r;
455
456     \$a = \$b;
457     \$b = \$r;
458     \$q = \$a/\$b;
459     \$r = \$a % \$b;
460
461   }
462
463   \$tempR;
464 }
465
466
467 1;
```

 aubreyja at gmail dot com ViewVC Help Powered by ViewVC 1.0.9