#
# This file is part of Language::Befunge.
# Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
#

package Language::Befunge::lib::MODU;

use strict;
use warnings;

use POSIX qw{ floor };

sub new { return bless {}, shift; }


# -- modulus

#
# $mod = M( $x, $y );
#
# signed-result modulo: x MOD y = x - FLOOR(x / y) * y
#
sub M {
    my ($self, $lbi) = @_;
    my $ip = $lbi->get_curip;
    my $y = $ip->spop;
    my $x = $ip->spop;
    my $mod = $y == 0
        ? 0
        : $x - floor($x/$y)*$y;
    $ip->spush($mod);
}


#
# $mod = U( $x, $y );
#
# Sam Holden's unsigned-result modulo... No idea who this Sam Holden is
# or if he has a special algorithm for this, therefore always returning
# absolute value of standard modulo.
#
sub U {
    my ($self, $lbi) = @_;
    my $ip = $lbi->get_curip;
    my $y = $ip->spop;
    my $x = $ip->spop;
    if ( $y == 0 ) {
        $ip->spush(0);
        return;
    }
    my $mod = $x % $y;
    $ip->spush(abs($mod));
}


#
# $mod = R( $x, $y );
#
# C-language integer remainder: old C leaves negative modulo undefined
# but C99 defines it as the same sign as the dividend so that's what we're
# going with.
#
sub R {
    my ($self, $lbi) = @_;
    my $ip = $lbi->get_curip;
    my $y = $ip->spop;
    my $x = $ip->spop;
    if ( $y == 0 ) {
        $ip->spush(0);
        return;
    }

    my $mod = $x % $y;
    if ( ($x <= 0 && $mod <= 0) || ($x >= 0 && $mod >= 0)) {
        $ip->spush( $mod );
    } else {
        $ip->spush( -$mod );
    }    
}


1;

__END__


=head1 NAME

Language::Befunge::IP::lib::MODU - Modulo Arithmetic extension



=head1 DESCRIPTION

The MODU fingerprint (0x4d4f4455) implements some of the finer, less-well-
agreed-upon points of modulo arithmetic. With positive arguments, these
instructions work exactly the same as C<%> does. However, when negative
values are involved, they all work differently.


=head1 FUNCTIONS

=head2 new

Create a new MODU instance.



=head2 Modulo implementations

=over 4

=item $mod = M( $x, $y )

Signed-result modulo: x MOD y = x - FLOOR(x / y) * y


=item $mod = U( $x, $y )

Sam Holden's unsigned-result modulo... No idea who this Sam Holden is
or if he has a special algorithm for this, therefore always returning
absolute value of standard modulo.


=item $mod = R( $x, $y )

C-language integer remainder: old C leaves negative modulo undefined
but C99 defines it as the same sign as the dividend so that's what we're
going with.


=back



=head1 SEE ALSO

L<Language::Befunge>, L<http://catseye.tc/projects/funge98/library/MODU.html>.



=head1 AUTHOR

Jerome Quelin, C<< <jquelin@cpan.org> >>



=head1 COPYRIGHT & LICENSE

Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.

This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.


=cut
