/*
 * $Id: math.ops 48214 2010-07-29 17:21:09Z NotFound $
** math.ops
*/

=head1 NAME

math.ops - Mathematical Opcodes

=cut

=head1 DESCRIPTION

Operations that perform basic mathematics. See F<src/dynoplibs/> for more
advanced operations.

When making changes to any ops file, run C<make bootstrap-ops> to regenerate
all generated ops files.

=head2 Arithmetic operations

These operations store the results of arithmetic on other registers and
constants into their destination register, $1.

=over 4

=cut

########################################

=item B<abs>(inout INT)

=item B<abs>(inout NUM)

=item B<abs>(invar PMC)

Set $1 to its absolute value.

=item B<abs>(out INT, in INT)

=item B<abs>(out NUM, in NUM)

=item B<abs>(out PMC, invar PMC)

Set $1 to absolute value of $2.

=cut

inline op abs(inout INT) :base_core {
    $1 = abs($1);
}

inline op abs(inout NUM) :base_core {
    $1 = fabs($1);
}

inline op abs(out INT, in INT) :base_core {
    $1 = abs($2);
}

inline op abs(out NUM, in NUM) :base_core {
    $1 = fabs($2);
}

inline op abs(invar PMC) :base_core {
    VTABLE_i_absolute(interp, $1);
}

inline op abs(out PMC, invar PMC) :base_core {
    $1 = VTABLE_absolute(interp, $2, $1);
}

########################################

=item B<add>(inout INT, in INT)

=item B<add>(inout NUM, in NUM)

=item B<add>(invar PMC, invar PMC)

=item B<add>(invar PMC, in INT)

=item B<add>(invar PMC, in NUM)

Increase $1 by the amount in $2.

=item B<add>(out INT, in INT, in INT)

=item B<add>(out NUM, in NUM, in NUM)

=item B<add>(invar PMC, invar PMC, invar PMC)

=item B<add>(invar PMC, invar PMC, in INT)

=item B<add>(invar PMC, invar PMC, in NUM)

Set $1 to the sum of $2 and $3.

=cut

inline op add(inout INT, in INT) :base_core {
    $1 += $2;
}

inline op add(inout NUM, in NUM) :base_core {
    $1 += $2;
}

inline op add(invar PMC, invar PMC) :base_core {
    VTABLE_i_add(interp, $1, $2);
}

inline op add(invar PMC, in INT) :base_core {
    VTABLE_i_add_int(interp, $1, $2);
}

inline op add(invar PMC, in NUM) :base_core {
    VTABLE_i_add_float(interp, $1, $2);
}

inline op add(out INT, in INT, in INT) :base_core {
    $1 = $2 + $3;
}

inline op add(out NUM, in NUM, in NUM) :base_core {
    $1 = $2 + $3;
}

inline op add(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_add(interp, $2, $3, $1);
}

inline op add(invar PMC, invar PMC, in INT) :base_core {
    $1 = VTABLE_add_int(interp, $2, $3, $1);
}

inline op add(invar PMC, invar PMC, in NUM) :base_core {
    $1 = VTABLE_add_float(interp, $2, $3, $1);
}

########################################

=item B<dec>(inout INT)

=item B<dec>(inout NUM)

=item B<dec>(invar PMC)

Decrease $1 by one.

=cut

inline op dec(inout INT) :base_core {
    $1--;
}

inline op dec(inout NUM) :base_core {
    $1--;
}

inline op dec(invar PMC) :base_core {
    VTABLE_decrement(interp, $1);
}

########################################

=item B<div>(inout INT, in INT)

=item B<div>(inout NUM, in NUM)

=item B<div>(invar PMC, invar PMC)

=item B<div>(invar PMC, in INT)

=item B<div>(invar PMC, in NUM)

Divide $1 by $2.

=item B<div>(out INT, in INT, in INT)

=item B<div>(out NUM, in NUM, in NUM)

=item B<div>(invar PMC, invar PMC, invar PMC)

=item B<div>(invar PMC, invar PMC, in INT)

=item B<div>(invar PMC, invar PMC, in NUM)

Set $1 to the quotient of $2 divided by $3. In the case of INTVAL division, the
result is truncated (NOT rounded or floored).
If the denominator is zero, a 'Divide by zero' exception is thrown.

=cut

inline op div(inout INT, in INT) :base_core {
    INTVAL den = $2;
    if (den == 0) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }
    $1 /= den;
}

inline op div(inout NUM, in NUM) :base_core {
    FLOATVAL den = $2;
    if (FLOAT_IS_ZERO(den)) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }
    $1 /= den;
}

inline op div(invar PMC, invar PMC) :base_core {
    VTABLE_i_divide(interp, $1, $2);
}

inline op div(invar PMC, in INT) :base_core {
    VTABLE_i_divide_int(interp, $1, $2);
}

inline op div(invar PMC, in NUM) :base_core {
    VTABLE_i_divide_float(interp, $1, $2);
}
inline op div(out INT, in INT, in INT) :base_core {
    INTVAL den = $3;
    if (den == 0) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }
    $1 = $2 / den;
}

inline op div(out NUM, in NUM, in NUM) :base_core {
    FLOATVAL den = $3;
    if (FLOAT_IS_ZERO(den)) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }
    $1 = $2 / den;
}

inline op div(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_divide(interp, $2, $3, $1);
}

inline op div(invar PMC, invar PMC, in INT) :base_core {
    $1 = VTABLE_divide_int(interp, $2, $3, $1);
}

inline op div(invar PMC, invar PMC, in NUM) :base_core {
    $1 = VTABLE_divide_float(interp, $2, $3, $1);
}


=item B<fdiv>(inout INT, in INT)

=item B<fdiv>(inout NUM, in NUM)

=item B<fdiv>(invar PMC, invar PMC)

=item B<fdiv>(invar PMC, in INT)

=item B<fdiv>(invar PMC, in NUM)

Floor divide $1 by $2.

=item B<fdiv>(out INT, in INT, in INT)

=item B<fdiv>(out NUM, in NUM, in NUM)

=item B<fdiv>(invar PMC, invar PMC, invar PMC)

=item B<fdiv>(invar PMC, invar PMC, in INT)

=item B<fdiv>(invar PMC, invar PMC, in NUM)

Set $1 to the quotient of $2 divided by $3. The result is the floor()
of the division i.e. the next whole integer towards -inf.
If the denominator is zero, a 'Divide by zero' exception is thrown.

=cut

inline op fdiv(inout INT, in INT) :base_core {
    INTVAL   den = $2;
    FLOATVAL f;

    if (den == 0) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }

    f  = floor(((FLOATVAL)$1) / den);
    $1 = (INTVAL)f;
}

inline op fdiv(inout NUM, in NUM) :base_core {
    FLOATVAL den = $2;
    if (FLOAT_IS_ZERO(den)) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }
    $1 = floor($1 / den);
}

inline op fdiv(invar PMC, invar PMC) :base_core {
    VTABLE_i_floor_divide(interp, $1, $2);
}

inline op fdiv(invar PMC, in INT) :base_core {
    VTABLE_i_floor_divide_int(interp, $1, $2);
}

inline op fdiv(invar PMC, in NUM) :base_core {
    VTABLE_i_floor_divide_float(interp, $1, $2);
}

inline op fdiv(out INT, in INT, in INT) :base_core {
    INTVAL   den = $3;
    FLOATVAL f;

    if (den == 0) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }

    f  = floor((FLOATVAL)$2 / den);
    $1 = (INTVAL)f;
}

inline op fdiv(out NUM, in NUM, in NUM) :base_core {
    FLOATVAL den = $3;
    if (FLOAT_IS_ZERO(den)) {
        opcode_t *handler = Parrot_ex_throw_from_op_args(interp, expr NEXT(),
            EXCEPTION_DIV_BY_ZERO,
            "Divide by zero");
        goto ADDRESS(handler);
    }
    $1 = floor($2 / den);
}

inline op fdiv(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_floor_divide(interp, $2, $3, $1);
}

inline op fdiv(invar PMC, invar PMC, in INT) :base_core {
    $1 = VTABLE_floor_divide_int(interp, $2, $3, $1);
}

inline op fdiv(invar PMC, invar PMC, in NUM) :base_core {
    $1 = VTABLE_floor_divide_float(interp, $2, $3, $1);
}

########################################

=item B<ceil>(inout NUM)

Set $1 to the smallest integral value greater than or equal to $1.

=item B<ceil>(out INT, in NUM)

=item B<ceil>(out NUM, in NUM)

Set $1 to the smallest integral value greater than or equal to $2.

=cut

inline op ceil(inout NUM) :base_core {
    $1 = ceil($1);
}

inline op ceil(out INT, in NUM) :base_core {
    FLOATVAL f = ceil($2);
    $1         = (INTVAL)f;
}

inline op ceil(out NUM, in NUM) :base_core {
    $1 = ceil($2);
}

########################################

=item B<floor>(inout NUM)

Set $1 to the largest integral value less than or equal to $1.

=item B<floor>(out INT, in NUM)

=item B<floor>(out NUM, in NUM)

Set $1 to the largest integral value less than or equal to $2.

=cut

inline op floor(inout NUM) :base_core {
    $1 = floor($1);
}

inline op floor(out INT, in NUM) :base_core {
    FLOATVAL f = floor($2);
    $1         = (INTVAL)f;
}

inline op floor(out NUM, in NUM) :base_core {
    $1 = floor($2);
}

########################################

=item B<inc>(inout INT)

=item B<inc>(inout NUM)

=item B<inc>(invar PMC)

Increase $1 by one.

=cut

inline op inc(inout INT) :base_core {
    $1++;
}

inline op inc(inout NUM) :base_core {
    $1++;
}

inline op inc(invar PMC) :base_core {
    VTABLE_increment(interp, $1);
}


########################################

=item B<mod>(out INT, in INT, in INT)

=item B<mod>(out NUM, in NUM, in NUM)

=item B<mod>(invar PMC, invar PMC, invar PMC)

=item B<mod>(invar PMC, invar PMC, in INT)

=item B<mod>(invar PMC, invar PMC, in NUM)

Sets $1 to the modulus of $2 and $3.

=item B<mod>(inout INT, in INT)

=item B<mod>(inout NUM, in NUM)

=item B<mod>(invar PMC, invar PMC)

=item B<mod>(invar PMC, in INT)

=item B<mod>(invar PMC, in NUM)

Sets $1 to the modulus of $1 and $2.


NOTE: This "corrected mod" algorithm is based on the C code on page 70
of [1]. Assuming correct behavior of the built-in mod operator (%) with
positive arguments, this algorithm implements a mathematically convenient
version of mod, defined thus:

  x mod y = x - y * floor(x / y)

For more information on this definition of mod, see section 3.4 of [2],
pages 81-85.

References:

  [1] Donald E. Knuth, *MMIXware: A RISC Computer for the Third
      Millennium* Springer, 1999.

  [2] Ronald L. Graham, Donald E. Knuth and Oren Patashnik, *Concrete
      Mathematics*, Second Edition. Addison-Wesley, 1994.

=cut

op mod(inout INT, in INT) :base_core {
    $1 = intval_mod($1, $2);
}

op mod(inout NUM, in NUM) :base_core {
    $1 = floatval_mod($1, $2);
}

inline op mod(invar PMC, invar PMC) :base_core {
    VTABLE_i_modulus(interp, $1, $2);
}

inline op mod(invar PMC, in INT) :base_core {
    VTABLE_i_modulus_int(interp, $1, $2);
}

inline op mod(invar PMC, in NUM) :base_core {
    VTABLE_i_modulus_float(interp, $1, $2);
}

op mod(out INT, in INT, in INT) :base_core {
    $1 = intval_mod($2, $3);
}

op mod(out NUM, in NUM, in NUM) :base_core {
    $1 = floatval_mod($2, $3);
}

inline op mod(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_modulus(interp, $2, $3, $1);
}

inline op mod(invar PMC, invar PMC, in INT) :base_core {
    $1 = VTABLE_modulus_int(interp, $2, $3, $1);
}

inline op mod(invar PMC, invar PMC, in NUM) :base_core {
    $1 = VTABLE_modulus_float(interp, $2, $3, $1);
}

########################################

=item B<mul>(inout INT, in INT)

=item B<mul>(inout NUM, in NUM)

=item B<mul>(invar PMC, invar PMC)

=item B<mul>(invar PMC, in INT)

=item B<mul>(invar PMC, in NUM)

Set $1 to the product of $1 and $2.

=item B<mul>(out INT, in INT, in INT)

=item B<mul>(out NUM, in NUM, in NUM)

=item B<mul>(invar PMC, invar PMC, invar PMC)

=item B<mul>(invar PMC, invar PMC, in INT)

=item B<mul>(invar PMC, invar PMC, in NUM)

Set $1 to the product of $2 and $3.

=cut

inline op mul(inout INT, in INT) :base_core {
    $1 *= $2;
}

inline op mul(inout NUM, in NUM) :base_core {
    $1 *= $2;
}

inline op mul(invar PMC, invar PMC) :base_core {
    VTABLE_i_multiply(interp, $1, $2);
}

inline op mul(invar PMC, in INT) :base_core {
    VTABLE_i_multiply_int(interp, $1, $2);
}

inline op mul(invar PMC, in NUM) :base_core {
    VTABLE_i_multiply_float(interp, $1, $2);
}

inline op mul(out INT, in INT, in INT) :base_core {
    $1 = $2 * $3;
}

inline op mul(out NUM, in NUM, in NUM) :base_core {
    $1 = $2 * $3;
}

inline op mul(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_multiply(interp, $2, $3, $1);
}

inline op mul(invar PMC, invar PMC, in INT) :base_core {
    $1 = VTABLE_multiply_int(interp, $2, $3, $1);
}

inline op mul(invar PMC, invar PMC, in NUM) :base_core {
    $1 = VTABLE_multiply_float(interp, $2, $3, $1);
}

########################################

=item B<neg>(inout INT)

=item B<neg>(inout NUM)

=item B<neg>(invar PMC)

Set $1 to its negative.

=item B<neg>(out INT, in INT)

=item B<neg>(out NUM, in NUM)

=item B<neg>(out PMC, invar PMC)

Set $1 to the negative of $2.

=cut

inline op neg(inout INT) :base_core {
    $1 = - $1;
}

inline op neg(inout NUM) :base_core {
    $1 = - $1;
}

inline op neg(invar PMC) :base_core {
    VTABLE_i_neg(interp, $1);
}

inline op neg(out INT, in INT) :base_core {
    $1 = - $2;
}

inline op neg(out NUM, in NUM) :base_core {
    $1 = - $2;
}

inline op neg(out PMC, invar PMC) :base_core {
    $1 = VTABLE_neg(interp, $2, $1);
}

########################################

=item B<sub>(inout INT, in INT)

=item B<sub>(inout NUM, in NUM)

=item B<sub>(invar PMC, invar PMC)

=item B<sub>(invar PMC, in INT)

=item B<sub>(invar PMC, in NUM)

Decrease $1 by the amount in $2.

=item B<sub>(out INT, in INT, in INT)

=item B<sub>(out NUM, in NUM, in NUM)

=item B<sub>(invar PMC, invar PMC, invar PMC)

=item B<sub>(invar PMC, invar PMC, in INT)

=item B<sub>(invar PMC, invar PMC, in NUM)

Set $1 to $2 minus $3.

=cut

inline op sub(inout INT, in INT) :base_core {
    $1 -= $2;
}

inline op sub(inout NUM, in NUM) :base_core {
    $1 -= $2;
}

inline op sub(invar PMC, invar PMC) :base_core {
    VTABLE_i_subtract(interp, $1, $2);
}

inline op sub(invar PMC, in INT) :base_core {
    VTABLE_i_subtract_int(interp, $1, $2);
}

inline op sub(invar PMC, in NUM) :base_core {
    VTABLE_i_subtract_float(interp, $1, $2);
}

inline op sub(out INT, in INT, in INT) :base_core {
    $1 = $2 - $3;
}

inline op sub(out NUM, in NUM, in NUM) :base_core {
    $1 = $2 - $3;
}

inline op sub(invar PMC, invar PMC, invar PMC) :base_core {
    $1 = VTABLE_subtract(interp, $2, $3, $1);
}

inline op sub(invar PMC, invar PMC, in INT) :base_core {
    $1 = VTABLE_subtract_int(interp, $2, $3, $1);
}

inline op sub(invar PMC, invar PMC, in NUM) :base_core {
    $1 = VTABLE_subtract_float(interp, $2, $3, $1);
}

########################################

=item B<sqrt>(out NUM, in NUM)

Set $1 to the square root of $2.

=cut

inline op sqrt(out NUM, in NUM) :base_core {
    $1 = sqrt((FLOATVAL)$2);
}

=back

=cut

###############################################################################

=head1 COPYRIGHT

Copyright (C) 2001-2009, Parrot Foundation.

=head1 LICENSE

This program is free software. It is subject to the same license
as the Parrot interpreter itself.

=cut

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
