package Rational;
use Carp qw(croak);
use overload('+' => \&op_add,
'+=' => \&op_addeq,
'++' => \&op_inc,
'*' => \&op_mul,
'*=' => \&op_muleq,
'-' => \&op_sutr,
'-=' => \&op_sutreq,
'--' => \&op_dec,
'/' => \&op_div,
'/=' => \&op_diveq,
'>' => \&op_gt,
'<' => \&op_lt,
'>=' => \&op_ge,
'<=' => \&op_le,
'==' => \&op_eq,
'!=' => \&op_ne,
'neg' => \&op_neg,
'=' => \&op_assign,
q("") => \&stringify,
q(0+) => \&numify,
'bool' => \&boolify);
my $r_1 = Rational->new(1,1);
my $r_0 = Rational->new(0,1);
sub new
{
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = [];
if(ref($invocant))
{
$self->[0] = $invocant->num();
$self->[1] = $invocant->den();
}
else
{
$self->[0] = shift || 0;
$self->[1] = shift || 1;
}
normalize($self);
bless ($self, $class);
return $self;
}
sub num
{
my $self = shift;
$self->[0];
}
sub den
{
my $self = shift;
$self->[1];
}
sub set_num
{
my $self = shift;
$self->[0] = shift;
normalize($self);
}
sub set_den
{
my $self = shift;
$self->[1] = shift;
normalize($self);
}
sub negate
{
my $self = shift;
$self->[0] = -$self->[0];
return $self;
}
sub invert
{
my $self = shift;
($self->[1],$self->[0]) = ($self->[0],$self->[1]);
if($self->[1] == 0)
{
croak "Zero denominator";
}
elsif($self->[1] < 0)
{
$self->[0] = - $self->[0];
$self->[1] = - $self->[1];
}
}
sub normalize
{
my $self = shift;
if ($self->[1] == 0)
{
croak("Zero denominator");
}
elsif($self->[1] < 0)
{
$self->[0] = -$self->[0];
$self->[1] = -$self->[1];
}
{
use integer;
my($a,$b) = ($self->[0],$self->[1]);
while($b)
{
my $r = $a % $b;
$r += $b if $r < 0;
$a = $b;
$b = $r;
}
}
if($r)
{
$self->[0] /= $r;
$self->[1] /= $r;
}
}
sub op_add
{
my($x,$y) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
add($x,$y,$r);
return $r;
}
sub op_addeq
{
my($x,$y) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
add($x,$y,$r);
return $r;
}
sub op_inc
{
my $x = shift;
add($x,$r_1,$x);
}
sub op_mul
{
my($x,$y) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
mul($x,$y,$r);
return $r;
}
sub op_muleq
{
my($x,$y) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
mul($x,$y,$r);
return $r;
}
sub op_sutr
{
my($x,$y,$swap) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
sutr($x,$y,$r);
$r = -$r if $swap;
return $r;
}
sub op_sutreq
{
my($x,$y,$swap) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
sutr($x,$y,$r);
$r = -$r if $swap;
return $r;
}
sub op_dec
{
my $x = shift;
sutr($x,$r_1,$x);
}
sub op_div
{
my($x,$y) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
div($x,$y,$r);
return $r;
}
sub op_diveq
{
my($x,$y) = @_;
unless(ref($y))
{
$y = Rational->new($y);
}
my $r = Rational->new();
div($x,$y,$r);
return $r;
}
sub op_neg
{
my $self = shift;
return $self->negate();
}
sub op_assign
{
my $self = shift;
return $self->new();
}
sub op_gt
{
my($x,$y) = @_;
return compare($x,$y) > 0;
}
sub op_lt
{
my($x,$y) = @_;
return compare($x,$y) < 0;
}
sub op_ge
{
my($x,$y) = @_;
return compare($x,$y) >= 0;
}
sub op_le
{
my($x,$y) = @_;
return compare($x,$y) <= 0;
}
sub op_eq
{
my($x,$y) = @_;
return compare($x,$y) == 0;
}
sub op_ne
{
my($x,$y) = @_;
return compare($x,$y) != 0;
}
sub stringify
{
my $self = shift;
return "$self->[0]/$self->[1]";
}
sub numify
{
my $self = shift;
return $self->[0]/$self->[1];
}
sub boolify
{
my $self = shift;
return $self->[0];
}
# takes three args
# Arg0: Rational X
# Arg1: Rational Y
# Arg2: Return Rational
sub add
{
my($x,$y,$r) = @_;
$r->[0] = $x->[0] * $y->[1];
$r->[1] = $x->[1] * $y->[0];
$r->[0] = $r->[0] + $r->[1];
$r->[1] = $x->[1] * $y->[1];
$r->normalize();
}
# takes three args
# Arg0: Rational X
# Arg1: Rational Y
# Arg2: Return Rational
sub mul
{
my($x,$y,$r) = @_;
$r->[0] = $x->[0] * $y->[0];
$r->[1] = $x->[1] * $y->[1];
$r->normalize();
}
# takes three args
# Arg0: Rational X
# Arg1: Rational Y
# Arg2: Return Rational
sub sutr
{
my($x,$y,$r) = @_;
$r->[0] = $x->[0] * $y->[1];
$r->[1] = $x->[1] * $y->[0];
$r->[0] = $r->[0] - $r->[1];
$r->[1] = $x->[1] * $y->[1];
$r->normalize();
}
# takes three args
# Arg0: Rational X
# Arg1: Rational Y
# Arg2: Return Rational
sub div
{
my($x,$y,$r) = @_;
$r->[0] = $x->[0] * $y->[1];
$r->[1] = $x->[1] * $y->[0];
$r->normalize();
}
# Takes 2 Rationals
# returns 1 if $x > $y
# returns 0 if $x == $y
# returns -1 if $x < $y
sub compare
{
my $x = shift;
my $y = shift;
return(($x->[0] * $y->[1]) <=> ($y->[0] * $x->[1]));
}
1;
|