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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5595 - (download) (as text) (annotate)
Thu Dec 20 00:08:17 2007 UTC (12 years, 2 months ago) by gage
File size: 10768 byte(s)
remove \ensuremath

from around the output.  I don't think it will work properly (although it might have worked with LatexToHTML)

    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 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 = " \\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 = "  -\\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 = " -$coeff \\frac{abs($tempNum)}{abs($tempDenom)} " if ($self->scalar < 0);
  345     $out = " $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