package goja

import (
	
	
	
)

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Abs(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Acos(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Acosh(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Asin(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Asinh(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Atan(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Atanh(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToFloat()
	 := .Argument(1).ToFloat()

	return floatToValue(math.Atan2(, ))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Cbrt(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Ceil(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return intToValue(int64(bits.LeadingZeros32(toUint32(.Argument(0)))))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Cos(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Cosh(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Exp(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Expm1(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Floor(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(float64(float32(.Argument(0).ToFloat())))
}

func ( *Runtime) ( FunctionCall) Value {
	var  float64
	var  bool
	 := make([]float64, 0, len(.Arguments))
	for ,  := range .Arguments {
		 := nilSafe().ToFloat()
		if math.IsNaN() {
			 = true
		} else {
			 := math.Abs()
			if  >  {
				 = 
			}
			 = append(, )
		}
	}
	if math.IsInf(, 1) {
		return _positiveInf
	}
	if  {
		return _NaN
	}
	if  == 0 {
		return _positiveZero
	}

	// Kahan summation to avoid rounding errors.
	// Normalize the numbers to the largest one to avoid overflow.
	var ,  float64
	for ,  := range  {
		 /= 
		 := * - 
		 :=  + 
		 = ( - ) - 
		 = 
	}
	return floatToValue(math.Sqrt() * )
}

func ( *Runtime) ( FunctionCall) Value {
	 := toUint32(.Argument(0))
	 := toUint32(.Argument(1))
	return intToValue(int64(int32( * )))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Log(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Log1p(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Log10(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Log2(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	 := math.Inf(-1)
	 := .Arguments
	for ,  := range  {
		 := nilSafe().ToFloat()
		if math.IsNaN() {
			 = [+1:]
			goto 
		}
		 = math.Max(, )
	}

	return floatToValue()

:
	// All arguments still need to be coerced to number according to the specs.
	for ,  := range  {
		nilSafe().ToFloat()
	}
	return _NaN
}

func ( *Runtime) ( FunctionCall) Value {
	 := math.Inf(1)
	 := .Arguments
	for ,  := range  {
		 := nilSafe().ToFloat()
		if math.IsNaN() {
			 = [+1:]
			goto 
		}
		 = math.Min(, )
	}

	return floatToValue()

:
	// All arguments still need to be coerced to number according to the specs.
	for ,  := range  {
		nilSafe().ToFloat()
	}
	return _NaN
}

func pow(,  Value) Value {
	if ,  := .(valueInt);  {
		if ,  := .(valueInt);  &&  >= 0 {
			if  == 0 {
				return intToValue(1)
			}
			if  == 0 {
				return intToValue(0)
			}
			 := ipow(int64(), int64())
			if  != 0 {
				return intToValue()
			}
		}
	}
	 := .ToFloat()
	 := .ToFloat()
	if math.Abs() == 1 && math.IsInf(, 0) {
		return _NaN
	}
	if  == 1 && math.IsNaN() {
		return _NaN
	}
	return floatToValue(math.Pow(, ))
}

func ( *Runtime) ( FunctionCall) Value {
	return pow(.Argument(0), .Argument(1))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(.rand())
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToFloat()
	if math.IsNaN() {
		return _NaN
	}

	if  == 0 && math.Signbit() {
		return _negativeZero
	}

	 := math.Trunc()

	if  >= 0 {
		if - >= 0.5 {
			return floatToValue( + 1)
		}
	} else {
		if - > 0.5 {
			return floatToValue( - 1)
		}
	}

	return floatToValue()
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0)
	 := .ToFloat()
	if math.IsNaN() ||  == 0 { // this will match -0 too
		return 
	}
	if  > 0 {
		return intToValue(1)
	}
	return intToValue(-1)
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Sin(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Sinh(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Sqrt(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Tan(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	return floatToValue(math.Tanh(.Argument(0).ToFloat()))
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0)
	if ,  := .(valueInt);  {
		return 
	}
	return floatToValue(math.Trunc(.ToFloat()))
}

func createMathTemplate() *objectTemplate {
	 := newObjectTemplate()
	.protoFactory = func( *Runtime) *Object {
		return .global.ObjectPrototype
	}

	.putStr("E", func( *Runtime) Value { return valueProp(valueFloat(math.E), false, false, false) })
	.putStr("LN10", func( *Runtime) Value { return valueProp(valueFloat(math.Ln10), false, false, false) })
	.putStr("LN2", func( *Runtime) Value { return valueProp(valueFloat(math.Ln2), false, false, false) })
	.putStr("LOG10E", func( *Runtime) Value { return valueProp(valueFloat(math.Log10E), false, false, false) })
	.putStr("LOG2E", func( *Runtime) Value { return valueProp(valueFloat(math.Log2E), false, false, false) })
	.putStr("PI", func( *Runtime) Value { return valueProp(valueFloat(math.Pi), false, false, false) })
	.putStr("SQRT1_2", func( *Runtime) Value { return valueProp(valueFloat(sqrt1_2), false, false, false) })
	.putStr("SQRT2", func( *Runtime) Value { return valueProp(valueFloat(math.Sqrt2), false, false, false) })

	.putSym(SymToStringTag, func( *Runtime) Value { return valueProp(asciiString(classMath), false, false, true) })

	.putStr("abs", func( *Runtime) Value { return .methodProp(.math_abs, "abs", 1) })
	.putStr("acos", func( *Runtime) Value { return .methodProp(.math_acos, "acos", 1) })
	.putStr("acosh", func( *Runtime) Value { return .methodProp(.math_acosh, "acosh", 1) })
	.putStr("asin", func( *Runtime) Value { return .methodProp(.math_asin, "asin", 1) })
	.putStr("asinh", func( *Runtime) Value { return .methodProp(.math_asinh, "asinh", 1) })
	.putStr("atan", func( *Runtime) Value { return .methodProp(.math_atan, "atan", 1) })
	.putStr("atanh", func( *Runtime) Value { return .methodProp(.math_atanh, "atanh", 1) })
	.putStr("atan2", func( *Runtime) Value { return .methodProp(.math_atan2, "atan2", 2) })
	.putStr("cbrt", func( *Runtime) Value { return .methodProp(.math_cbrt, "cbrt", 1) })
	.putStr("ceil", func( *Runtime) Value { return .methodProp(.math_ceil, "ceil", 1) })
	.putStr("clz32", func( *Runtime) Value { return .methodProp(.math_clz32, "clz32", 1) })
	.putStr("cos", func( *Runtime) Value { return .methodProp(.math_cos, "cos", 1) })
	.putStr("cosh", func( *Runtime) Value { return .methodProp(.math_cosh, "cosh", 1) })
	.putStr("exp", func( *Runtime) Value { return .methodProp(.math_exp, "exp", 1) })
	.putStr("expm1", func( *Runtime) Value { return .methodProp(.math_expm1, "expm1", 1) })
	.putStr("floor", func( *Runtime) Value { return .methodProp(.math_floor, "floor", 1) })
	.putStr("fround", func( *Runtime) Value { return .methodProp(.math_fround, "fround", 1) })
	.putStr("hypot", func( *Runtime) Value { return .methodProp(.math_hypot, "hypot", 2) })
	.putStr("imul", func( *Runtime) Value { return .methodProp(.math_imul, "imul", 2) })
	.putStr("log", func( *Runtime) Value { return .methodProp(.math_log, "log", 1) })
	.putStr("log1p", func( *Runtime) Value { return .methodProp(.math_log1p, "log1p", 1) })
	.putStr("log10", func( *Runtime) Value { return .methodProp(.math_log10, "log10", 1) })
	.putStr("log2", func( *Runtime) Value { return .methodProp(.math_log2, "log2", 1) })
	.putStr("max", func( *Runtime) Value { return .methodProp(.math_max, "max", 2) })
	.putStr("min", func( *Runtime) Value { return .methodProp(.math_min, "min", 2) })
	.putStr("pow", func( *Runtime) Value { return .methodProp(.math_pow, "pow", 2) })
	.putStr("random", func( *Runtime) Value { return .methodProp(.math_random, "random", 0) })
	.putStr("round", func( *Runtime) Value { return .methodProp(.math_round, "round", 1) })
	.putStr("sign", func( *Runtime) Value { return .methodProp(.math_sign, "sign", 1) })
	.putStr("sin", func( *Runtime) Value { return .methodProp(.math_sin, "sin", 1) })
	.putStr("sinh", func( *Runtime) Value { return .methodProp(.math_sinh, "sinh", 1) })
	.putStr("sqrt", func( *Runtime) Value { return .methodProp(.math_sqrt, "sqrt", 1) })
	.putStr("tan", func( *Runtime) Value { return .methodProp(.math_tan, "tan", 1) })
	.putStr("tanh", func( *Runtime) Value { return .methodProp(.math_tanh, "tanh", 1) })
	.putStr("trunc", func( *Runtime) Value { return .methodProp(.math_trunc, "trunc", 1) })

	return 
}

var mathTemplate *objectTemplate
var mathTemplateOnce sync.Once

func getMathTemplate() *objectTemplate {
	mathTemplateOnce.Do(func() {
		mathTemplate = createMathTemplate()
	})
	return mathTemplate
}

func ( *Runtime) () *Object {
	 := .global.Math
	if  == nil {
		 = &Object{runtime: }
		.global.Math = 
		.newTemplatedObject(getMathTemplate(), )
	}
	return 
}