[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 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