package Math::AnyInt; #use 5.006; use v5.36; use strict; use warnings; our $VERSION = '0.01'; use Exporter qw(import); our @EXPORT = qw( calcop VERBOSE_AnyInt ); # caller can set this to 0 or 1 our $VERBOSE_AnyInt = 0; # overload basic arithmetic operators use overload '+' => sub { # we get 3 params: 1+2 are objects of this class, swap is ... well # get the value of the objects, do the operation # and then convert to 32bit my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "+ : operating on ".(defined($self)&&defined($$self)?$$self:'')." and ".(defined($other)?$other:'')." (swap is ".(defined($swap)?($swap?'yes':'no'):'').")\n" } Math::AnyInt->new( calc_32bit_signed($self->value() + $other->value()) ); }, '-' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "- : operating on ".(defined($self)&&defined($$self)?$$self:'')." and ".(defined($other)?$other:'')." (swap is ".(defined($swap)?($swap?'yes':'no'):'').")\n" } return $swap ? Math::AnyInt->new( calc_32bit_signed($self->value() - $other->value()) ) : Math::AnyInt->new( calc_32bit_signed($other->value() - $self->value()) ) ; }, '*' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "* : operating on ".(defined($self)&&defined($$self)?$$self:'')." and ".(defined($other)?$other:'')." (swap is ".(defined($swap)?($swap?'yes':'no'):'').")\n" } Math::AnyInt->new( calc_32bit_signed($self->value() + $other->value()) ); }, '/' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "/ : operating on ".(defined($self)&&defined($$self)?$$self:'')." and ".(defined($other)?$other:'')." (swap is ".(defined($swap)?($swap?'yes':'no'):'').")\n" } return $swap ? Math::AnyInt->new( calc_32bit_signed($self->value() / $other->value()) ) : Math::AnyInt->new( calc_32bit_signed($other->value() / $self->value()) ) ; }, '<<' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "<< : operating on ".(defined($self)&&defined($$self)?$$self:'')." and ".(defined($other)?$other:'')." (swap is ".(defined($swap)?($swap?'yes':'no'):'').")\n" } # if operands were swapped, it does matter in this case if( $swap ){ die "<< : order is important why is swap on?" } Math::AnyInt->new( calc_32bit_signed($self->value() << $other->value()) ); }, '>>' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT ">> : operating on ".(defined($self)&&defined($$self)?$$self:'')." and ".(defined($other)?$other:'')." (swap is ".(defined($swap)?($swap?'yes':'no'):'').")\n" } if( $swap ){ die "<< : order is important why is swap on?" } Math::AnyInt->new( calc_32bit_signed($self->value() >> $other->value()) ); }, '**' => sub { my ($self, $other, $swap) = @_; if( $VERBOSE_AnyInt > 0 ){ print STDOUT "** : operating on ".(defined($self)&&defined($$self)?$$self:'')." and ".(defined($other)?$other:'')." (swap is ".(defined($swap)?($swap?'yes':'no'):'').")\n" } if( $swap ){ die "<< : order is important why is swap on?" } Math::AnyInt->new( calc_32bit_signed($self->value() ** $other->value()) ); }, '""' => sub { ${$_[0]} } ; sub new { my ($class, $val) = @_; return bless \$val } # Corion's converting to 32bit signed int sub calc_32bit_signed { return unpack("l", pack("L", $_[0])) } ##sub calc_32bit_signed { unpack( "l", pack "L", ($_[0] & 0xffffffff)) } # NERDVANA's converting to 32bit signed int #sub calc_32bit_signed { return calc_32bit_signed_in_C($_[0]) } use Inline 'C', <<~'END'; #include long calc_32bit_signed_in_C(long input) { return (int32_t)((int32_t)input); } long test_addition_in_C(long l, long r) { return (int32_t)((int32_t)l + (int32_t)r); } END # return the integer value contain in this object # which is just a ref to the value (see new()) sub value { return ${ $_[0] } } # this does the arithmetic when the operator is in string format, useful for testing sub calcop { my ($lop, $op, $rop) = @_; if( ref($lop) ne __PACKAGE__ ){ die 'calcop()'." : left-operant must be a ".__PACKAGE__." object but it is just '".ref($lop)."'."; } if( ref($rop) ne __PACKAGE__ ){ die 'calcop()'." : right-operant must be a ".__PACKAGE__." object but it is just '".ref($rop)."'."; } if( $VERBOSE_AnyInt > 0 ){ print STDOUT 'calcop()'." : called for '$$lop' '$op' '$$rop' ...\n" } if( $op eq '+' ){ return $lop + $rop; } elsif( $op eq '-' ){ return $lop - $rop } elsif( $op eq '*' ){ return $lop * $rop } elsif( $op eq '/' ){ return $lop / $rop } elsif( $op eq '<<' ){ return $lop << $rop } elsif( $op eq '>>' ){ return $lop >> $rop } elsif( $op eq '**' ){ return $lop ** $rop } die 'calcop()'." : unknown operator '$op'."; } 1; #### #!/usr/bin/env perl use strict; use warnings; use lib 'blib/lib'; our $VERSION = '0.01'; use Test::More; use Math::AnyInt; $Math::AnyInt::VERBOSE_AnyInt = 1; my @tests = ( # left-operand, operator, right-operand, expected result in JS [1, '+', 2, 3], [1169367104, '<<', 5, -1234958336], [1073741824, '+', 1073741824, 2147483648], ); for my $at (@tests){ my ($lop, $op, $rop, $expected) = @$at; my $LOP = Math::AnyInt->new($lop); is($$LOP, $lop, 'Math::AnyInt->new()'." : called and contains value '$lop'."); my $ROP = Math::AnyInt->new($rop); is($$ROP, $rop, 'Math::AnyInt->new()'." : called and contains value '$rop'."); my $result = Math::AnyInt::calcop($LOP, $op, $ROP); is($$result, $expected, "$lop $op $rop : got $result, expected $expected."); } # test addition of two integers by taking it in C via Inline::C # and using C's int32_t (NERDVANA) is(Math::AnyInt::test_addition_in_C(1073741824, 1073741824), 2147483648, "Testing NERDVANA's C additon calc()"); # END done_testing();