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