/*
 * $Id: /mirror/trunk/src/ops/math.ops 35884 2009-01-21T08:02:35.116993Z chromatic  $
** math.ops
*/

VERSION = PARROT_VERSION;

=head1 NAME

math.ops - Mathematical Operations

=cut

=head1 DESCRIPTION

Operations that perform some sort of mathematics, including both basic
math and transcendental functions.

=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 {
  if ($2 < 0)
    $1 = - (INTVAL)$2;
  else
    $1 = (INTVAL)$2;
}

inline op abs(out NUM, in NUM) :base_core {
  if ($2 < 0)
    $1 = - (FLOATVAL)$2;
  else
    $1 = (FLOATVAL)$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<cmod>(out INT, in INT, in INT)

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

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

NOTE: This "uncorrected mod" algorithm uses the C language's built-in
mod operator (x % y), which is

    ... the remainder when x is divided by y, and thus is zero
    when y divides x exactly.
    ...
    The direction of truncation for / and the sign of the result
    for % are machine-dependent for negative operands, as is the
    action taken on overflow or underflow.
                                                     -- [1], page 41

Also:

    ... if the second operand is 0, the result is undefined.
    Otherwise, it is always true that (a/b)*b + a%b is equal to z. If
    both operands are non-negative, then the remainder is non-
    negative and smaller than the divisor; if not, it is guaranteed
    only that the absolute value of the remainder is smaller than
    the absolute value of the divisor.
                                                     -- [1], page 205

This op is provided for those who need it (such as speed-sensitive
applications with heavy use of mod, but using it only with positive
arguments), but a more mathematically useful mod based on ** floor(x/y)
and defined with y == 0 is provided by the mod op.

  [1] Brian W. Kernighan and Dennis M. Ritchie, *The C Programming
      Language*, Second Edition. Prentice Hall, 1988.

If the denominator is zero, a 'Divide by zero' exception is thrown.

=cut

inline op cmod(out INT, in INT, in INT) :base_core {
  INTVAL den = $3;
  if ($3 == 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 cmod(invar PMC, invar PMC, in INT) :base_core {
  INTVAL result;

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

  result = VTABLE_get_integer(interp, $2) % $3;

  $1 = pmc_new(interp, VTABLE_type(interp, $2));
  VTABLE_set_integer_native(interp, $1, result);
}

inline op cmod(invar PMC, invar PMC, invar PMC) :base_core {
  INTVAL result;
  INTVAL value = VTABLE_get_integer(interp, $3);

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

  result = VTABLE_get_integer(interp, $2) % value;

  $1 = pmc_new(interp, VTABLE_type(interp, $2));
  VTABLE_set_integer_native(interp, $1, result);
}

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

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

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

NOTE: This "uncorrected mod" algorithm uses the built-in C math library's
fmod() function, which computes

    ... the remainder of dividing x by y. The return value is
    x - n * y, where n is the quotient of x / y, rounded towards
    zero to an integer.
                                -- fmod() manpage on RedHat Linux 7.0

In addition, fmod() returns

    the remainder, unless y is zero, when the function fails and
    errno is set.

According to page 251 of [1], the result when y is zero is implementation-
defined.

This op is provided for those who need it, but a more mathematically
useful numeric mod based on floor(x/y) instead of truncate(x/y) and
defined with y == 0 is provided by the mod op.

  [1] Brian W. Kernighan and Dennis M. Ritchie, *The C Programming
      Language*, Second Edition. Prentice Hall, 1988.

If the denominator is zero, a 'Divide by zero' exception is thrown.

=cut

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

inline op cmod(invar PMC, invar PMC, in NUM) :base_core {
  FLOATVAL result;
  FLOATVAL value = $3;

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

  result = fmod(VTABLE_get_integer(interp, $2), value);

  $1 = pmc_new(interp, VTABLE_type(interp, $2));
  VTABLE_set_integer_native(interp, $1, (INTVAL) result);
}

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

=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($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($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<pow>(out NUM, in NUM, in NUM)

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

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

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

Set $1 to $2 raised to the power $3.

=cut

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

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

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

inline op pow(invar PMC, invar PMC, in NUM) :base_core {
      $1 = VTABLE_pow_float(interp, $2, $3, $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



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

=head2 Transcendental mathematical operations

These operations perform various transcendental operations such as logarithmics
and trigonometrics.

=over 4

=cut

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

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

Set $1 to the arc cosine (in radians) of $2.

=cut

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

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

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

Set $1 to the arc secant (in radians) of $2.

=cut

inline op asec(out NUM, in NUM) :base_math {
  $1 = acos(((FLOATVAL)1) / ((FLOATVAL)$2));
}

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


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

Set $1 to the arc sine (in radians) of $2.

=cut

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

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

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

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

The two-argument versions set $1 to the arc tangent (in radians) of $2.

The three-argument versions set $1 to the arc tangent (in radians) of
$2 / $3, taking account of the signs of the arguments in determining the
quadrant of the result.

=cut

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

inline op atan(out NUM, in NUM, in NUM) :base_math {
  $1 = atan2((FLOATVAL)$2, (FLOATVAL)$3);
}

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

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

Set $1 to the cosine of $2 (given in radians).

=cut

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

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

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

Set $1 to the hyperbolic cosine of $2 (given in radians).

=cut

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

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

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

Set $1 to I<e> raised to the power $2. I<e> is the base of the natural
logarithm.

=cut

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

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

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

Set $1 to the natural (base I<e>) logarithm of $2.

=cut

inline op ln(out NUM, in NUM) :base_math {
  $1 = log((FLOATVAL)$2);
}

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

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

Set $1 to the base 10 logarithm of $2.

=cut

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

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

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

Set $1 to the base 2 logarithm of $2.

=cut

op log2(out NUM, in NUM) :base_math {
   FLOATVAL temp = log((FLOATVAL)2.0);
  $1 = log((FLOATVAL)$2) / temp;
}

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

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

Set $1 to the secant of $2 (given in radians).

=cut

inline op sec(out NUM, in NUM) :base_math {
  $1 = ((FLOATVAL)1) / cos((FLOATVAL)$2);
}

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

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

Set $1 to the hyperbolic secant of $2 (given in radians).

=cut

inline op sech(out NUM, in NUM) :base_math {
  $1 = ((FLOATVAL)1) / cosh((FLOATVAL)$2);
}

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

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

Set $1 to the sine of $2 (given in radians).

=cut

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

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

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

Set $1 to the hyperbolic sine of $2 (given in radians).

=cut

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

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

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

Set $1 to the tangent of $2 (given in radians).

=cut

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

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

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

Set $1 to the hyperbolic tangent of $2 (given in radians).

=cut

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

=back

=cut

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

=head2 Other mathematical operations

Implementations of various mathematical operations

=over 4

=cut

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

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

Greatest Common divisor of $2 and $3.

=cut

inline op gcd(out INT, in INT, in INT) :advanced_math {
 INTVAL p = 0;
 INTVAL a = $2 < 0 ? -$2 : $2;
 INTVAL b = $3 < 0 ? -$3 : $3;

 if (a==0) { $1=b; goto NEXT(); }
 if (b==0) { $1=a; goto NEXT(); }

 while (!((a | b) & 1)) {
   a>>=1;
   b>>=1;
   p++;
 }

 while (a>0) {
   if (!(a & 1)) a>>=1;
   else if (!(b & 1)) b>>=1;
   else if (a<b)      b = (b-a)>>1;
   else               a = (a-b)>>1;
 }

 $1 = b<<p;
}


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

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

Least Common Multiple of $2 and $3

=cut

inline op lcm(out INT, in INT, in INT) :advanced_math {
 INTVAL gcd = 0;
 INTVAL p = 0;
 INTVAL a = $2 < 0 ? -$2 : $2;
 INTVAL b = $3 < 0 ? -$3 : $3;
 INTVAL saved_var1 = a, saved_var2 = b;

 if (a==0 || b==0) { $1=0; goto NEXT(); }

 while (!((a | b) & 1)) {
   a>>=1;
   b>>=1;
   p++;
 }

 while (a>0) {
   if (!(a & 1)) a>>=1;
   else if (!(b & 1)) b>>=1;
   else if (a<b)      b = (b-a)>>1;
   else               a = (a-b)>>1;
 }

 gcd = b<<p;
 saved_var1 /= gcd;
 $1 = saved_var1*saved_var2;
}

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

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

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

Factorial, n!. Calculates the product of 1 to N.

=cut

inline op fact(out INT, in INT) :advanced_math {
  /* Coercing a negative to a UINT can get pretty ugly
   * in this situation. */
  INTVAL i = $2;
  UINTVAL q = 1;
  while (i>0) {
    q = q*i;
    i--;
  }
  $1 = q;
}

inline op fact(out NUM, in INT) :advanced_math {
  /* Coercing a negative to a UINT can get pretty ugly
   * in this situation. */
  INTVAL i = $2;
  FLOATVAL q = 1;
  while (i>0) {
    q = q*i;
    i--;
  }
  $1 = q;
}

=back

=cut

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

=head1 COPYRIGHT

Copyright (C) 2001-2008, The Perl 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:
 */
