mirror of
https://github.com/perlbot/perlbuut
synced 2025-06-08 17:45:39 -04:00
430 lines
14 KiB
Perl
430 lines
14 KiB
Perl
#!/usr/bin/perl
|
|
|
|
package Math::Farnsworth::Value;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Data::Dumper;
|
|
|
|
use Math::Pari;
|
|
use Math::Farnsworth::Dimension;
|
|
use Date::Manip;
|
|
use List::Util qw(sum);
|
|
use Storable qw(dclone);
|
|
|
|
use utf8;
|
|
|
|
our $VERSION = 0.5;
|
|
|
|
use overload
|
|
'+' => \&add,
|
|
'-' => \&subtract,
|
|
'*' => \&mult,
|
|
'/' => \&div,
|
|
'%' => \&mod,
|
|
'**' => \&pow,
|
|
'<=>' => \&compare,
|
|
'bool' => \&bool;
|
|
|
|
sub new
|
|
{
|
|
my $class = shift;
|
|
my $value = shift;
|
|
my $dimen = shift; #should only really be used internally?
|
|
my $outmagic = shift; #i'm still not sure on this one
|
|
|
|
my $self = {};
|
|
|
|
bless $self, $class;
|
|
|
|
$self->{outmagic} = $outmagic;
|
|
$self->{valueinput} = $value;
|
|
|
|
if (ref($dimen) eq "Math::Farnsworth::Dimension")
|
|
{
|
|
$self->{dimen} = $dimen;
|
|
}
|
|
else
|
|
{
|
|
$dimen = {} if !defined($dimen);
|
|
$self->{dimen} = new Math::Farnsworth::Dimension($dimen);
|
|
}
|
|
|
|
if (exists($self->{dimen}{dimen}{string}) || exists($self->{dimen}{dimen}{date}) || exists($self->{dimen}{dimen}{lambda}))
|
|
{
|
|
$self->{pari} = $value;
|
|
}
|
|
elsif (exists($self->{dimen}{dimen}{array}))
|
|
{
|
|
#we've got an array or date or lambda here
|
|
#use dclone() from Storable to make complete copies of everything!
|
|
# print "CALLER: ".join("::", caller())."\n";
|
|
# print Dumper($value);
|
|
|
|
if (!defined($value))
|
|
{
|
|
#warn "OMG \$VALUE IS UNDEFINED!!!!";
|
|
$value = [];
|
|
}
|
|
|
|
$self->{pari} = [@{$value}]; #this should never not be an array reference
|
|
}
|
|
elsif (!defined($value))
|
|
{
|
|
$self->{pari} = undef;
|
|
}
|
|
else
|
|
{
|
|
$value =~ s/ee/e/i; #fixes double ee's
|
|
$self->{pari} = PARI $value;
|
|
}
|
|
|
|
|
|
return $self;
|
|
}
|
|
|
|
sub toperl
|
|
{
|
|
my $self = shift;
|
|
|
|
return "".($self->{pari}); #stringifiying it seems to work, though i need cases for arrays!
|
|
}
|
|
|
|
sub add
|
|
{
|
|
my ($one, $two, $rev) = @_;
|
|
|
|
#check for $two being a simple value
|
|
my $tv = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{pari} : $two;
|
|
|
|
#i also need to check the units, but that will come later
|
|
#NOTE TO SELF this needs to be more helpful, i'll probably do something by adding stuff in ->new to be able to fetch more about the processing
|
|
#print Dumper($one->{dimen}, $two->{dimen});
|
|
die "Unable to process different units in addition\n" unless ($one->{dimen}->compare($two->{dimen}) || $one->{dimen}{dimen}{date}); #always call this on one, since $two COULD be some other object
|
|
|
|
#moving this down so that i don't do any math i don't have to
|
|
|
|
my $new;
|
|
if ($one->{dimen}{dimen}{string})
|
|
{
|
|
#print Dumper($one, $two);
|
|
$new = new Math::Farnsworth::Value($one->{pari} . $tv, $one->{dimen});
|
|
}
|
|
elsif ($one->{dimen}{dimen}{array})
|
|
{
|
|
die "Adding arrays is undefined behavoir!\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{bool})
|
|
{
|
|
die "Adding Booleans is undefined behavoir!\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{lambda})
|
|
{
|
|
die "Adding Lambdas is undefined behavoir.\n";
|
|
}
|
|
elsif (($one->{dimen}{dimen}{date}) && ($two->{dimen}{dimen}{date}))
|
|
{
|
|
die "Adding of two dates is unsupported\n";
|
|
}
|
|
elsif (($one->{dimen}{dimen}{date}) && ($two->{dimen}->compare({dimen=>{time => 1}}))) #check if we are adding time to a date
|
|
{
|
|
my $seconds = $two->toperl(); #calling WITHOUT the units SHOULD give me a string containing just the number
|
|
$seconds =~ s/\..*$//; #filter off fractional parts of a second as Date::Manip hates them
|
|
my $sign = $seconds > 0 ? "+" : "-"; #should work fine, even with loss of precision
|
|
my $delta = DateCalc($one->{pari}, "$sign $seconds seconds"); #order is switched, to make it work the way I think it should
|
|
|
|
$new = new Math::Farnsworth::Value($delta, {date=>1});
|
|
}
|
|
else
|
|
{
|
|
$new = new Math::Farnsworth::Value($one->{pari} + $tv, $one->{dimen});
|
|
}
|
|
|
|
return $new;
|
|
}
|
|
|
|
sub subtract
|
|
{
|
|
my ($one, $two, $rev) = @_;
|
|
|
|
#check for $two being a simple value
|
|
my $tv = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{pari} : $two;
|
|
|
|
#i also need to check the units, but that will come later
|
|
#NOTE TO SELF this needs to be more helpful, i'll probably do something by adding stuff in ->new to be able to fetch more about the processing
|
|
die "Unable to process different units in subtraction\n" unless ($one->{dimen}->compare($two->{dimen}) || $one->{dimen}{dimen}{date}); #always call this on one, sinc
|
|
|
|
#moving this down so that i don't do any math i don't have to
|
|
my $new;
|
|
if (!$rev)
|
|
{
|
|
if (($one->{dimen}{dimen}{date}) && ($two->{dimen}{dimen}{date}))
|
|
{
|
|
my $delta = DateCalc($two->{pari}, $one->{pari}); #order is switched, to make it work the way I think it should
|
|
die "something went screwy with calculating deltas, $delta\n" unless $delta =~ /^[+-](\d+:){6}(\d+)$/;
|
|
|
|
my $seconds = new Math::Farnsworth::Value(Delta_Format($delta,1,"%st"), {time => 1});
|
|
$new = $seconds; #create that
|
|
}
|
|
elsif (($one->{dimen}{dimen}{date}) && ($two->{dimen}->compare({dimen => {time => 1}})))
|
|
{
|
|
my $seconds = $two->toperl(); #calling WITHOUT the units SHOULD give me a string containing just the number
|
|
$seconds =~ s/\..*$//; #filter off fractional parts of a second as Date::Manip hates them
|
|
my $sign = $seconds > 0 ? "-" : "+"; #should work fine, even with loss of precision
|
|
my $delta = DateCalc($one->{pari}, "$sign $seconds seconds"); #order is switched, to make it work the way I think it should
|
|
|
|
$new = new Math::Farnsworth::Value($delta, {date=>1});
|
|
}
|
|
elsif ($one->{dimen}{dimen}{date})
|
|
{
|
|
#we reached here with some other subtraction with a Date, do not do it
|
|
die "Dates can only have dates and time subtracted, nothing else\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{array} || $two->{dimen}{dimen}{array})
|
|
{
|
|
#we reached here with some other subtraction with a Date, do not do it
|
|
die "subtracting arrays is undefined behavior\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{string} || $two->{dimen}{dimen}{string})
|
|
{
|
|
#we reached here with some other subtraction with a Date, do not do it
|
|
die "Subtracting strings is undefined behavior\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{bool} || $two->{dimen}{dimen}{bool})
|
|
{
|
|
#we reached here with some other subtraction with a Date, do not do it
|
|
die "Subtracting Booleans is undefined behavior\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{lambda} || $two->{dimen}{dimen}{lambda})
|
|
{
|
|
#we reached here with some other subtraction with a Date, do not do it
|
|
die "Subtracting lambdas is undefined behavior\n";
|
|
}
|
|
else
|
|
{
|
|
$new = new Math::Farnsworth::Value($one->{pari} - $tv, $one->{dimen}); #if !$rev they are in order
|
|
}
|
|
}
|
|
else
|
|
{
|
|
die "some mistake happened here in subtraction\n"; #to test later on
|
|
# $new = new Math::Farnsworth::Value($tv - $one->{pari}, $one->{dimen}); #if !$rev they are in order
|
|
}
|
|
return $new;
|
|
}
|
|
|
|
sub mod
|
|
{
|
|
my ($one, $two, $rev) = @_;
|
|
|
|
#check for $two being a simple value
|
|
my $tv = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{pari} : $two;
|
|
|
|
#i also need to check the units, but that will come later
|
|
#NOTE TO SELF this needs to be more helpful, i'll probably do something by adding stuff in ->new to be able to fetch more about the processing
|
|
die "Unable to process different units in modulous\n" unless $one->{dimen}->compare($two->{dimen}); #always call this on one, since $two COULD be some other object
|
|
|
|
if ($one->{dimen}->compare({dimen => {string => 1}}) ||$one->{dimen}->compare({dimen => {array =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {string => 1}}) ||$two->{dimen}->compare({dimen => {array =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {bool => 1}}) ||$one->{dimen}->compare({dimen => {bool =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {lambda => 1}}) ||$one->{dimen}->compare({dimen => {lambda =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {date => 1}}) ||$one->{dimen}->compare({dimen => {date =>1}}))
|
|
{
|
|
die "Can't divide arrays or strings or booleans or dates or lambdas, it doesn't make sense\n";
|
|
}
|
|
|
|
#moving this down so that i don't do any math i don't have to
|
|
my $new;
|
|
if (!$rev)
|
|
{
|
|
$new = new Math::Farnsworth::Value($one->{pari} % $tv, $one->{dimen}); #if !$rev they are in order
|
|
}
|
|
else
|
|
{
|
|
$new = new Math::Farnsworth::Value($tv % $one->{pari}, $one->{dimen}); #if !$rev they are in order
|
|
}
|
|
return $new;
|
|
}
|
|
|
|
sub mult
|
|
{
|
|
my ($one, $two, $rev) = @_;
|
|
|
|
#check for $two being a simple value
|
|
my $tv = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{pari} : $two;
|
|
my $td = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{dimen} : new Math::Farnsworth::Dimension();
|
|
|
|
if ($one->{dimen}->compare({dimen => {string => 1}}) ||$one->{dimen}->compare({dimen => {array =>1}}) ||
|
|
$td->compare({dimen => {string => 1}}) ||$td->compare({dimen => {array =>1}}) ||
|
|
$td->compare({dimen => {bool => 1}}) ||$one->{dimen}->compare({dimen => {bool =>1}}) ||
|
|
$td->compare({dimen => {lambda => 1}}) ||$one->{dimen}->compare({dimen => {lambda =>1}}) ||
|
|
$td->compare({dimen => {date => 1}}) ||$one->{dimen}->compare({dimen => {date =>1}})) {
|
|
die "Can't multiply arrays or strings, it doesn't make sense\n";
|
|
}
|
|
|
|
my $nd = $one->{dimen}->merge($td); #merge the dimensions! don't cross the streams though
|
|
|
|
#moving this down so that i don't do any math i don't have to
|
|
my $new = new Math::Farnsworth::Value($one->{pari} * $tv, $nd);
|
|
return $new;
|
|
}
|
|
|
|
sub div
|
|
{
|
|
my ($one, $two, $rev) = @_;
|
|
|
|
#check for $two being a simple value
|
|
my $tv = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{pari} : $two;
|
|
my $td = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{dimen} : new Math::Farnsworth::Dimension();
|
|
|
|
if ($one->{dimen}->compare({dimen => {string => 1}}) ||$one->{dimen}->compare({dimen => {array =>1}}) ||
|
|
$td->compare({dimen => {string => 1}}) ||$td->compare({dimen => {array =>1}}) ||
|
|
$td->compare({dimen => {bool => 1}}) ||$one->{dimen}->compare({dimen => {bool =>1}}) ||
|
|
$td->compare({dimen => {lambda => 1}}) ||$one->{dimen}->compare({dimen => {lambda =>1}}) ||
|
|
$td->compare({dimen => {date => 1}}) ||$one->{dimen}->compare({dimen => {date =>1}}))
|
|
{
|
|
die "Can't divide arrays or strings, it doesn't make sense\n";
|
|
}
|
|
|
|
#these are a little screwy SO i'll probably comment them more later
|
|
#probably after i find out that they're wrong
|
|
my $qd = $rev ? $td : $one->{dimen};
|
|
my $dd = $rev ? $one->{dimen}->invert() : (ref($td) eq "HASH" ? $td : $td->invert());
|
|
|
|
my $nd;
|
|
|
|
if (ref($qd) ne "HASH")
|
|
{
|
|
$nd = $qd->merge($dd); #merge the dimensions! don't cross the streams though
|
|
}
|
|
else
|
|
{
|
|
$nd = $dd->merge($qd); #merge them the other way, because $qd is a "HASH" and not an object
|
|
}
|
|
|
|
#moving this down so that i don't do any math i don't have to
|
|
my $new;
|
|
if (!$rev)
|
|
{
|
|
$new = new Math::Farnsworth::Value($one->{pari} / $tv, $nd); #if !$rev they are in order
|
|
}
|
|
else
|
|
{
|
|
$new = new Math::Farnsworth::Value($tv / $one->{pari}, $nd); #if !$rev they are in order
|
|
}
|
|
|
|
return $new;
|
|
}
|
|
|
|
sub bool
|
|
{
|
|
my $self = shift;
|
|
|
|
#seems good enough of an idea to me
|
|
#i have a bug HERE
|
|
#print "BOOLCONV\n";
|
|
#print Dumper($self);
|
|
#print "ENDBOOLCONV\n";
|
|
return $self->{pari}?1:0;
|
|
}
|
|
|
|
sub pow
|
|
{
|
|
my ($one, $two, $rev) = @_;
|
|
|
|
if ($one->{dimen}->compare({dimen => {string => 1}}) ||$one->{dimen}->compare({dimen => {array =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {string => 1}}) ||$two->{dimen}->compare({dimen => {array =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {bool => 1}}) ||$one->{dimen}->compare({dimen => {bool =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {lambda => 1}}) ||$one->{dimen}->compare({dimen => {lambda =>1}}) ||
|
|
$two->{dimen}->compare({dimen => {date => 1}}) ||$one->{dimen}->compare({dimen => {date =>1}}))
|
|
{
|
|
die "Can't exponentiate arrays or strings or dates or bools, it doesn't make sense\n";
|
|
}
|
|
|
|
#check for $two being a simple value
|
|
my $tv = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{pari} : $two;
|
|
my $td = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{dimen} : undef;
|
|
|
|
if (defined($td) && !$td->compare({dimen=>{}}))
|
|
{
|
|
die "A number with units as the exponent doesn't make sense";
|
|
}
|
|
|
|
#moving this down so that i don't do any math i don't have to
|
|
my $new;
|
|
if (!$rev)
|
|
{
|
|
$new = new Math::Farnsworth::Value($one->{pari} ** $tv, $one->{dimen}->mult($tv)); #if !$rev they are in order
|
|
}
|
|
else
|
|
{
|
|
# print Dumper(\@_);
|
|
#print "POW: $tv :: $two :: $one\n";
|
|
$new = new Math::Farnsworth::Value($tv ** $one->{pari}, $one->{dimen}->mult($tv)); #if !$rev they are in order
|
|
}
|
|
|
|
return $new;
|
|
}
|
|
|
|
sub compare
|
|
{
|
|
my ($one, $two, $rev) = @_;
|
|
|
|
my $rv = $rev ? -1 : 1;
|
|
#check for $two being a simple value
|
|
my $tv = ref($two) && $two->isa("Math::Farnsworth::Value") ? $two->{pari} : $two;
|
|
|
|
#i also need to check the units, but that will come later
|
|
#NOTE TO SELF this needs to be more helpful, i'll probably do something by adding stuff in ->new to be able to fetch more about the processing
|
|
die "Unable to process different units in compare\n" unless $one->{dimen}->compare($two->{dimen}); #always call this on one, since $two COULD be some other object
|
|
|
|
#moving this down so that i don't do any math i don't have to
|
|
my $new;
|
|
|
|
if ($one->{dimen}{dimen}{string})
|
|
{
|
|
if ($one->{pari} eq $tv)
|
|
{
|
|
$new = 0;
|
|
}
|
|
elsif ($one->{pari} lt $tv)
|
|
{
|
|
$new = -1;
|
|
}
|
|
elsif ($one->{pari} gt $tv)
|
|
{
|
|
$new = 1;
|
|
}
|
|
}
|
|
elsif ($one->{dimen}{dimen}{array})
|
|
{
|
|
die "Comparing arrays has not been implemented\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{lambdas})
|
|
{
|
|
die "Comparing lambdas has not been implemented\n";
|
|
}
|
|
elsif ($one->{dimen}{dimen}{date})
|
|
{
|
|
return Date_Cmp($one->{pari}, $two->{pari}); #does it for me!
|
|
}
|
|
else
|
|
{
|
|
if ($one->{pari} == $tv)
|
|
{
|
|
$new = 0;
|
|
}
|
|
elsif ($one->{pari} < $tv)
|
|
{
|
|
$new = -1;
|
|
}
|
|
elsif ($one->{pari} > $tv)
|
|
{
|
|
$new =1;
|
|
}
|
|
}
|
|
|
|
return $new;
|
|
}
|
|
|