/*
 * Copyright (C) 1999-2005 Chris Ross
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * o Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * o Neither the name of the ferite software nor the names of its contributors may
 *   be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

uses "math.lib";

module-header
{

#include <math.h>
#include <stdlib.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

}

/**
 * @namespace Math
 * @brief Provides a set of mathematical functions
 */
namespace Math
{
    /**
     * @function abs
     * @declaration function abs( number num )
     * @brief Calculates the absolute value of a number
     * @param number num The number to calculate the absolute value of
     * @return The absolute value of the number
     */
    native function abs( number num )
    {
	if( params[0]->type == F_VAR_DOUBLE )
            FE_RETURN_DOUBLE( fabs( num ) );
        FE_RETURN_LONG( labs( (long)num ) );
    }

    /**
     * @function acos
     * @declaration function acos( number degree )
     * @brief Calculates the arc cosine of a number
     * @param number degree The number to calculate the arc cosine of
     * @return The arc cosine of the specified value in radians
     * @description This function calculates the arc cosine of the specified
     *              value and returns it in radians. Note: The input value
     *              must be between -1 and 1.
     */
    native function acos( number degree )
    {
        if( degree < -1 || degree > 1 )
          ferite_error( script, 0, "Math.acos() can only take values between -1 and 1\n" );
        else
        {
            FE_RETURN_DOUBLE( acos( degree ) );
        }
    }

    /**
     * @function asin
     * @declaration function asin( number degree )
     * @brief Calculates the arc sine of a number
     * @param number degree The number to calculate the arc sine of
     * @return The arc sine of the specified value in radians
     * @description This function calculates the arc sine of the specified
     *              value and returns it in radians. Note: The input value
     *              must be between -1 and 1.
     */
    native function asin( number degree )
    {
        if( degree < -1 || degree > 1 )
          ferite_error( script, 0, "Math.asin() can only take values between -1 and 1\n" );
        else
        {
            FE_RETURN_DOUBLE( asin( degree ) );
        }
    }

    /**
     * @function atan
     * @declaration function atan( number degree )
     * @brief Calculates the arc tangent of a number
     * @param number degree The number to calculate the arc tangent of
     * @return The arc tangent of the specified value in radians
     */
    native function atan( number degree )
    {
        if( degree < -1 || degree > 1 )
            ferite_error( script, 0, "Math.atan() can only take values between -1 and 1\n" );
        else
        {
            FE_RETURN_DOUBLE( atan( degree ) );
        }
    }

    /**
     * @function atan2
     * @declaration function atan2( number x, number y )
     * @brief Calculates the arc tangent of two numbers
     * @param number x The first number to use in the calculation
     * @param number y The second number to use in the calculation
     * @return The arc tangent of the two values in radians
     * @description This function calculates the arc tangent of the two
     *              specified variables. It is like calculating the arc
     *              tangent of x/y, but the signs of both arguments are used
     *              when determining what quadrant the result is in.
     */
    native function atan2( number x, number y )
    {
        FE_RETURN_DOUBLE( atan2( x, y ) );
    }

    /**
     * @function ceil
     * @declaration function ceil( number num )
     * @brief Rounds a number up to the nearest integer value
     * @param number num The number
     * @return The smallest integer that is greater than or equal to the input
     */
    native function ceil( number num )
    {
        FE_RETURN_DOUBLE( ceil( num ) );
    }

    /**
     * @function cos
     * @declaration function cos( number degree )
     * @brief Calculates the cosine of a number
     * @param number degree The angle in radians to calculate the cosine of
     * @return The cosine of the specified angle as a number between -1 and 1
     */
    native function cos( number degree )
    {
        FE_RETURN_DOUBLE( cos( degree ) );
    }

    /**
     * @function exp
     * @declaration function exp( number power )
     * @brief Calculates the value of e raised to the specified power
     * @param number power The value to raise e to the power of
     * @return e (the base of natural logrithms) raised to the specified power
     */
    native function exp( number power )
    {
        FE_RETURN_DOUBLE( exp( power ) );
    }
    
    /**
     * @function floor
     * @declaration function floor( number num )
     * @brief Rounds a number down to the nearest integer value
     * @param number num The number
     * @return The largest integer that is smaller than or equal to the input
     */
    native function floor( number num )
    {
        FE_RETURN_DOUBLE( floor( num ) );
    }

    /**
     * @function getRandMax
     * @declaration function getRandMax( )
     * @brief Finds the maximum value that Math.rand() can return
     * @return The largest value that Math.rand() can return
     */
    native function getRandMax()
    {
        FE_RETURN_LONG( RAND_MAX );
    }

    /**
     * @function log
     * @declaration function log( number num )
     * @brief Calculates the natural logarithm of a number
     * @param number num The number
     * @return The natural logarithm of the specified number
     */
    native function log( number num )
    {
        if( num <= 0 )
          ferite_error( script, 0, "Math.log() can only take positive values\n" );
        else
        {
            FE_RETURN_DOUBLE( log( num ) );
        }
    }

    /**
     * @function log10
     * @declaration function log10( number num )
     * @brief Calculates the base 10 logarithm of a number
     * @param number num The number
     * @return The base 10 logarithm of the specified number
     */
    native function log10( number num )
    {
        if( num <= 0 )
          ferite_error( script, 0, "Math.log10() can only take positive values\n" );
        else
        {
            FE_RETURN_DOUBLE( log10( num ) );
        }
    }

    /**
     * @function max
     * @declaration function max( number num1, number num2 )
     * @brief Determines which of two numbers is the larger
     * @param number num1 The first number
     * @param number num2 The second number
     * @return The value of the larger of the two numbers
     */
    native function max( number num1, number num2 )
    {
        if( num1 < num2 )
        {
            FE_RETURN_DOUBLE( num2 );
        }
        else
        {
            FE_RETURN_DOUBLE( num1 );
        }
    }

    /**
     * @function min
     * @declaration function min( number num1, number num2 )
     * @brief Determines which of two numbers is the smaller
     * @param number num1 The first number
     * @param number num2 The second number
     * @return The value of the smaller of the two numbers
     */
    native function min( number num1, number num2 )
    {
        if( num1 > num2 )
        {
            FE_RETURN_DOUBLE( num2 );
        }
        else
        {
            FE_RETURN_DOUBLE( num1 );
        }
    }

    /**
     * @function pi
     * @declaration function pi()
     * @brief Returns the number Pi
     */
    /* APH: Why is this a function instead of a constant? */
    native function pi()
    {
        FE_RETURN_DOUBLE( M_PI );
    }

    /**
     * @function pow
     * @declaration function pow( number base, number exp )
     * @brief Calculates the value of a number raised to a power
     * @param number base The number
     * @param number exp The power to raise the number to
     * @return The value of base raised to the power of exp
     */
    native function pow( number base, number exp )
    {
        FE_RETURN_DOUBLE( pow( base, exp ) );
    }

    /**
     * @function rand
     * @declaration function rand()
     * @brief Generates a pseudo-random number
     * @return a pseudo-random number
     * @description This function returns a pseudo-random number. Before using
     *              it, you should call Math.srand() to seed the pseudo-random
     *              number generator. The value returned will always be between
     *              0 and the value returned by Math.getRandMax().
     */
    native function rand()
    {
        FE_RETURN_LONG( rand() );
    }

    /**
     * @function srand
     * @declaration function srand( number seed )
     * @brief Seeds the pseudo-random number generator
     * @param number seed The number to seed the pseudo-random number generator with
     * @description This function should be called before using Math.rand() to
     *              seed the pseudo-random number generator. It is common
     *              practice to use the current time, as returned by the
     *              function Sys.Time() as a seed for many tasks. Note that the
     *              sequence of numbers from the random number generator is
     *              determined by the seed, so if you reseed it with the a value
     *              you used previously the same sequence of random numbers will
     *              be generated. This is sometimes useful behaviour because it
     *              allows you to reproduce a "random" sequence any time simply
     *              by remembering what the seed value was.
     */
    native function srand( number seed )
    {
        srand( (long)seed );
    }

    /**
     * @function round
     * @declaration function round( number num )
     * @brief Rounds a number to the nearest integer
     * @param number num The number
     * @return The number rounded to the nearest integer
     * @description This function differs from Math.ceil() and Math.floor()
     *              because it will round either up or down, depending on
     *              whether the input is less than or greater than half way to
     *              the next integer up. Note that the current implementation
     *              will break if the input number is greater than that which
     *              can be represented by a signed long (about 2 billion on a
     *              32 bit processor).
     */
    native function round( number num )
    {
        int sign = (num < 0 ? -1 : 1 );
        FE_RETURN_LONG( (long)(num+(0.5 * sign)) );
    }

    /**
     * @function sin
     * @declaration function sin( number degree )
     * @brief Calculates the sine of a number
     * @param number degree The angle in radians to calculate the sine of
     * @return The sine of the specified angle as a number between -1 and 1
     */
    native function sin( number degree )
    {
        FE_RETURN_DOUBLE( sin( degree ) );
    }

    /**
     * @function sqrt
     * @declaration function sqrt( number num )
     * @brief Calculates the square root of a number
     * @param number num The number (must not be negative)
     * @return The square root of the specified number
     */
    native function sqrt( number num )
    {
        if( num < 0 )
          ferite_error( script, 0, "Math.sqrt() can only take positive values\n" );
        else
        {
            FE_RETURN_DOUBLE( sqrt( num ) );
        }
    }

    /**
     * @function tan
     * @declaration function tan( number degree )
     * @brief Calculates the tangent of a number
     * @param number degree The angle in radians to calculate the tangent of
     * @return The tangent of the specified angle as a number between -1 and 1
     */
    native function tan( number degree )
    {
        FE_RETURN_DOUBLE( tan( degree ) );
    }

    /**
     * @function isDouble
     * @declaration function isDouble( number n )
     * @brief Checks if a number is represented internally as a double
     * @param number n The number
     * @return True if the number is stored as a double, false if it is not
     */
    native function isDouble( number n )
    {
        if( params[0]->type == F_VAR_DOUBLE )
        {
            FE_RETURN_TRUE;
        }
        FE_RETURN_FALSE;
    }

    /**
     * @function isLong
     * @declaration function isLong( number n )
     * @brief Checks if a number is represented internally as a long
     * @param number n The number
     * @return True if the number is stored as a long, false if it is not
     */
    native function isLong( number n )
    {
        if( params[0]->type == F_VAR_LONG )
        {
            FE_RETURN_TRUE;
        }
        FE_RETURN_FALSE;
    }
}

uses "complex";
