# Source: Math.js

    /**
     * @license
     * Copyright 2020 Roberto Luiz Souza Monteiro,
     *                Renata Souza Barreto,
     *                Hernane Borges de Barros Pereira.
     *
     * Licensed under the Apache License, Version 2.0 (the 'License');
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an 'AS IS' BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    /**
     * MaiaScript math library.
     * @class
     */
    function Mathematics() {
        this.E       = Math.E;
        this.PI      = Math.PI;
        this.SQRT2   = Math.SQRT2;
        this.SQRT1_2 = Math.SQRT1_2;
        this.LN2     = Math.LN2;
        this.LN10    = Math.LN10;
        this.LOG2E   = Math.LOG2E;
        this.LOG10E  = Math.LOG10E;

        init();

        /**
         * Creates the attributes of the class.
         */
        function init() {
            // Class attributes goes here.
        }

       /*
        * Complex numbers functions:
        *
        * (a+b*i)/(c+d*i)=(a*c+b*d)/(c*c+d*d)+i*(b*c-a*d)/(c*c+d*d)
        *
        * cos(a+b*i)=cos(a)*cosh(b)-i*sin(a)*sinh(b)
        * sin(a+b*i)=sin(a)*cosh(b)+i*cos(a)*sinh(b)
        * tan(a+b*i)=sin(a+b*i)/cos(a+b*i)
        *
        * cosh(a+b*i)=cosh(a)*cos(b)+i*sinh(a)sin(b)
        * sinh(a+b*i)=sinh(a)*cos(b)+i*cosh(a)sin(b)
        * tanh(a+b*i)=sinh(a+b*i)/cosh(a+b*i)
        *
        * r=abs(a+b*i)=sqrt(a*a+b*b)
        * t=arg(a+b*i)=atan(b/a)
        *
        * exp(a+b*i)=exp(a)*cos(b)+i*sin(b)
        * log(a+b*i)=log(r)+i*t
        *
        * pow(a+b*i,n)=pow(r,n)*cos(n*t)+i*pow(r,n)*sin(n*t)
        * sqrt(a+b*i)=sqrt(r)*cos(t/2)+i*sqrt(r)*sin(t/2)
        *
        */

        /**
         * Returns the positive value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The positive value of x.
         */
        this.abs = function(x)
        {
            var y;
            if (core.type(x) == 'complex') {
                y = Math.sqrt(x.real * x.real + x.imaginary * x.imaginary);
            } else {
                y = Math.abs(x);
            }
            return y;
        }

        /**
         * Returns the arccosine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The arccosine value of x.
         */
        this.acos = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.acos(x);
            }
            return y;
        }

        /**
         * Returns the hyperbolic arccosine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The hyperbolic arccosine value of x.
         */
        this.acosh = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.acosh(x);
            }
            return y;
        }
        /**
         * Returns the complex number argument.
         * @param {object}   x - Value of X.
         * @return {number}  The complex number argument.
         */
        this.arg = function(x)
        {
            // t=arg(a+b*i)=atan(b/a)
            var y;
            if (core.type(x) == 'complex') {
                y = Math.atan(x.imaginary / x.real);
            }
            return y;
        }

        /**
         * Returns the arcsine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The arcsine value of x.
         */
        this.asin = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.asin(x);
            }
            return y;
        }

        /**
         * Returns the hyperbolic arcsine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The hyperbolic arcsine value of x.
         */
        this.asinh = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.asinh(x);
            }
            return y;
        }

        /**
         * Returns the arctangent value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The arctangent value of x.
         */
        this.atan = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.atan(x);
            }
            return y;
        }

        /**
         * Returns the arctangent of the quotient of x and y.
         * @param {object}   x - Value of X.
         * @param {object}   y - Value of Y.
         * @return {number}  The arctangent value of x.
         */
        this.atan2 = function(x, y)
        {
            var z;
            if (core.type(x) == 'number') {
                z = Math.atan2(x, y);
            }
            return z;
        }

        /**
         * Returns the hyperbolic arctangent value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The hyperbolic arctangent value of x.
         */
        this.atanh = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.atanh(x);
            }
            return y;
        }

        /**
         * Returns the cubic root of x.
         * @param {object}   x - Value of X.
         * @return {number}  The cubic root of x.
         */
        this.cbrt = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.cbrt(x);
            }
            return y;
        }

        /**
         * Returns the cosine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The cosine value of x.
         */
        this.cos = function(x)
        {
            // cos(a+b*i)=cos(a)*cosh(b)-i*sin(a)*sinh(b)
            var y;
            if (core.type(x) == 'complex') {
                y = core.complex(Math.cos(x.real) * Math.cosh(x.imaginary), Math.sin(x.real) * Math.sinh(x.imaginary));
            } else {
                y = Math.cos(x);
            }
            return y;
        }

        /**
         * Returns the hyperbolic cosine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The hyperbolic cosine value of x.
         */
        this.cosh = function(x)
        {
            // cosh(a+b*i)=cosh(a)*cos(b)+i*sinh(a)sin(b)
            var y;
            if (core.type(x) == 'complex') {
                y = core.complex(Math.cosh(x.real) * Math.cos(x.imaginary), Math.sinh(x.real) * Math.sin(x.imaginary));
            } else {
                y = Math.cosh(x);
            }
            return y;
        }

        /**
         * Returns the value of x rounded up.
         * @param {object}   x - Value of X.
         * @return {number}  Value of x rounded up.
         */
        this.ceil = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.ceil(x);
            }
            return y;
        }

        /**
         * Returns the value of E^x
         * @param {object}   x - Value of X.
         * @return {number}  Value of E^x.
         */
        this.exp = function(x)
        {
            // exp(a+b*i)=exp(a)*cos(b)+i*sin(b)
            var y;
            if (core.type(x) == 'complex') {
                y = core.complex(Math.exp(x.real) * Math.cos(x.imaginary), Math.sin(x.imaginary));
            } else {
                y = Math.exp(x);
            }
            return y;
        }

        /**
         * Returns the value of x rounded down.
         * @param {object}   x - Value of X.
         * @return {number}  Value of x rounded down.
         */
        this.floor = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.floor(x);
            }
            return y;
        }

        /**
         * Returns the value of the natural logarithm of x.
         * @param {object}   x - Value of X.
         * @return {number}  The value of the natural logarithm of x.
         */
        this.log = function(x)
        {
            // r=abs(a+b*i)=sqrt(a*a+b*b)
            // t=arg(a+b*i)=atan(b/a)
            // log(a+b*i)=log(r)+i*t
            var y;
            if (core.type(x) == 'complex') {
                var r = this.abs(x);
                var t = this.arg(x);
                y = core.complex(Math.log(r), t);
            } else {
                y = Math.log(x);
            }
            return y;
        }

        /**
         * Returns the largest value between x and y.
         * @param {object}   x - Value of X.
         * @param {object}   y - Value of y.
         * @return {number}  The largest value between x and y.
         */
        this.max = function(x, y)
        {
            var y;
            if ((core.type(x) == 'number') && (core.type(y) == 'number')) {
                y = Math.max(x, y);
            }
            return y;
        }

        /**
         * Returns the smallest value between x and y.
         * @param {object}   x - Value of X.
         * @param {object}   y - Value of y.
         * @return {number}  The smallest value between x and y.
         */
        this.min = function(x, y)
        {
            var y;
            if ((core.type(x) == 'number') && (core.type(y) == 'number')) {
                y = Math.min(x, y);
            }
            return y;
        }

        /**
         * Returns the value of x to the power of y.
         * @param {object}   x - Value of X.
         * @param {object}   y - Value of y.
         * @return {number}  Value of x to the power of y.
         */
        this.pow = function(x, y)
        {
            var z = core.power(x, y);
            return z;
        }

        /**
         * Returns a random number between 0 and 1.
         * @return {number}  A random number.
         */
        this.random = function()
        {
            var y = Math.random();
            return y;
        }

        /**
         * Returns the value of x rounding to the nearest value.
         * @param {object}   x - Value of X.
         * @return {number}  Value of x rounding to the nearest value.
         */
        this.round = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.round(x);
            }
            return y;
        }

        /**
         * Returns the sine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The sine value of x.
         */
        this.sin = function(x)
        {
            // sin(a+b*i)=sin(a)*cosh(b)+i*cos(a)*sinh(b)
            var y;
            if (core.type(x) == 'complex') {
                y = core.complex(Math.sin(x.real) * Math.cosh(x.imaginary), Math.cos(x.real) * Math.sinh(x.imaginary));
            } else {
                y = Math.sin(x);
            }
            return y;
        }

        /**
         * Returns the hyperbolic sine value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The hyperbolic sine value of x.
         */
        this.sinh = function(x)
        {
            // sinh(a+b*i)=sinh(a)*cos(b)+i*cosh(a)sin(b)
            var y;
            if (core.type(x) == 'complex') {
                y = core.complex(Math.sinh(x.real) * Math.cos(x.imaginary), Math.cosh(x.real) * Math.sin(x.imaginary));
            } else {
                y = Math.sinh(x);
            }
            return y;
        }

        /**
         * Returns the square root of x.
         * @param {object}   x - Value of X.
         * @return {number}  Value of the square root of x.
         */
        this.sqrt = function(x)
        {
            // r=abs(a+b*i)=sqrt(a*a+b*b)
            // t=arg(a+b*i)=atan(b/a)
            // sqrt(a+b*i)=sqrt(r)*cos(t/2)+i*sqrt(r)*sin(t/2)
            var y;
            if (core.type(x) == 'complex') {
                var r = this.abs(x);
                var t = this.arg(x);
                y = core.complex(Math.sqrt(r) * Math.cos(t / 2), Math.sqrt(r) * Math.sin(t / 2));
            } else {
                y = Math.sqrt(x);
            }
            return y;
        }

        /**
         * Returns the tangent value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The tangent value of x.
         */
        this.tan = function(x)
        {
            // tan(a+b*i)=sin(a+b*i)/cos(a+b*i)
            var y;
            if (core.type(x) == 'complex') {
                y = core.div(this.sin(x), this.cos(x));
            } else {
                y = Math.tan(x);
            }
            return y;
        }

        /**
         * Returns the hyperbolic tangent value of x.
         * @param {object}   x - Value of X.
         * @return {number}  The hyperbolic tangent value of x.
         */
        this.tanh = function(x)
        {
            // tanh(a+b*i)=sinh(a+b*i)/cosh(a+b*i)
            var y;
            if (core.type(x) == 'complex') {
                y = core.div(this.sinh(x), this.cosh(x));
            } else {
                y = Math.tanh(x);
            }
            return y;
        }

        /**
         * Returns the integer part of a number.
         * @param {object}   x - Value of X.
         * @return {number}  The integer part of a number
         */
        this.trunc = function(x)
        {
            var y;
            if (core.type(x) == 'number') {
                y = Math.trunc(x);
            }
            return y;
        }
    }

    math = new Mathematics();