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

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

 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;