using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using MathNet.Numerics.Random;
using Scriban.Runtime;
using Scriban.Syntax;
namespace Kalk.Core
{
///
/// Mathematical module (builtin).
///
public partial class MathModule : KalkModuleWithFunctions
{
public const string CategoryMathConstants = "Math Constants";
public const string CategoryMathFunctions = "Math Functions";
private readonly Func FibFunc;
private readonly Func AbsFunc;
private readonly Func RndFunc;
private static readonly Func CosFunc = Math.Cos;
private static readonly Func AcosFunc = Math.Acos;
private static readonly Func CoshFunc = Math.Cosh;
private static readonly Func AcoshFunc = Math.Acosh;
private static readonly Func SinFunc = Math.Sin;
private static readonly Func AsinFunc = Math.Asin;
private static readonly Func SinhFunc = Math.Sinh;
private static readonly Func AsinhFunc = Math.Asinh;
private static readonly Func TanFunc = Math.Tan;
private static readonly Func TanhFunc = Math.Tanh;
private static readonly Func AtanFunc = Math.Atan;
private static readonly Func AtanhFunc = Math.Atanh;
private static readonly Func RsqrtFunc = x => 1.0 / Math.Sqrt(x);
private static readonly Func SqrtFunc = Math.Sqrt;
private static readonly Func LogFunc = Math.Log;
private static readonly Func Log10Func = Math.Log10;
private static readonly Func Log2Func = Math.Log2;
private static readonly Func ExpFunc = Math.Exp;
private static readonly Func Exp2Func = x => Math.Pow(2, x);
private static readonly Func FracFunc = FracImpl;
private static readonly Func FracSignedFunc = FracSignedImpl;
private static readonly Func RoundFunc = Math.Round;
private static readonly Func FloorFunc= Math.Floor;
private static readonly Func CeilFunc = Math.Ceiling;
private static readonly Func TruncFunc= Math.Truncate;
private readonly Func SignFunc;
private static readonly Func RadiansFunc = x => x * Math.PI / 180.0;
private static readonly Func DegreesFunc = x => x * 180.0 / Math.PI;
private static readonly Func IsFiniteFunc = IsFiniteFuncImpl;
private static readonly Func IsInfFunc = IsInfFuncImpl;
private static readonly Func IsNanFunc = IsNanFuncImpl;
private static readonly Func SaturateFunc = SaturateImpl;
private Random _random;
public MathModule()
{
IsBuiltin = true;
FibFunc = Fibonacci;
AbsFunc = AbsImpl;
SignFunc = SignFuncImpl;
_random = new Random();
RndFunc = RndImpl;
RegisterFunctionsAuto();
}
///
/// Defines the "Not a Number" constant for a double.
///
///
/// ```kalk
/// >>> nan
/// # nan
/// out = nan
/// ```
///
[KalkExport("nan", CategoryMathFunctions)] public const double Nan = double.NaN;
///
/// Defines the infinity constant for a double.
///
///
/// ```kalk
/// >>> inf
/// # inf
/// out = inf
/// ```
///
[KalkExport("inf", CategoryMathFunctions)] public const double Inf = double.PositiveInfinity;
///
/// Defines the PI constant. pi = 3.14159265358979
///
///
/// ```kalk
/// >>> pi
/// # pi
/// out = 3.141592653589793
/// ```
///
[KalkExport("pi", CategoryMathFunctions)] public const double Pi = Math.PI;
///
/// Defines the natural logarithmic base. e = 2.71828182845905
///
///
/// ```kalk
/// >>> e
/// # e
/// out = 2.718281828459045
/// ```
///
[KalkExport("e", CategoryMathFunctions)] public const double E = Math.E;
///
/// Calculates the fibonacci number for the specified input.
///
/// The input number.
/// The fibonacci number.
///
/// ```kalk
/// >>> fib 50
/// # fib(50)
/// out = 12_586_269_025
/// ```
///
[KalkExport("fib", CategoryMathFunctions, Functor = true)]
public object Fib(KalkIntValue x) => x.TransformArg(Engine, FibFunc);
///
/// Defines the imaginary part of a complex number.
///
/// A complex number.
///
/// ```kalk
/// >>> 1 + 2i
/// # 1 + 2 * i
/// out = 1 + 2i
/// ```
///
[KalkExport("i", CategoryMathFunctions)]
public static object ComplexNumber()
{
return new KalkComplex(0, 1);
}
///
/// Determines if all components of the specified value are non-zero.
///
/// The specified value.
/// true if all components of the x parameter are non-zero; otherwise, false.
/// This function is similar to the `any` function.
/// The `any` function determines if any components of the specified value are non-zero, while the `all` function determines if all components of the specified value are non-zero.
///
///
/// ```kalk
/// >>> all(bool4(true, false, true, false))
/// # all(bool4(true, false, true, false))
/// out = false
/// >>> all(bool4(true, true, true, true))
/// # all(bool4(true, true, true, true))
/// out = true
/// >>> all([0,1,0,2])
/// # all([0,1,0,2])
/// out = false
/// >>> all([1,1,1,1])
/// # all([1,1,1,1])
/// out = true
/// ```
///
[KalkExport("all", CategoryMathFunctions)]
public KalkBool All(object x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (x is IEnumerable it)
{
foreach(var item in it)
{
if (!Engine.ToBool(Engine.CurrentSpan, item))
{
return false;
}
}
return true;
}
return Engine.ToBool(Engine.CurrentSpan, x);
}
///
/// Determines if any components of the specified value are non-zero.
///
/// The specified value.
/// true if any components of the x parameter are non-zero; otherwise, false.
/// This function is similar to the `all` intrinsic function.
/// The `any` function determines if any components of the specified value are non-zero,
/// while the `all` function determines if all components of the specified value are non-zero.
///
///
/// ```kalk
/// >>> any(bool4(true, false, true, false))
/// # any(bool4(true, false, true, false))
/// out = true
/// >>> any(bool4(false, false, false, false))
/// # any(bool4(false, false, false, false))
/// out = false
/// >>> any([0,1,0,2])
/// # any([0,1,0,2])
/// out = true
/// >>> any([0,0,0,0])
/// # any([0,0,0,0])
/// out = false
/// ```
///
[KalkExport("any", CategoryMathFunctions)]
public KalkBool Any(object x)
{
if (x == null) throw new ArgumentNullException(nameof(x));
if (x is IEnumerable it)
{
foreach (var item in it)
{
if (Engine.ToBool(Engine.CurrentSpan, item))
{
return true;
}
}
return false;
}
return Engine.ToBool(Engine.CurrentSpan, x);
}
///
/// Returns the absolute value of the specified value.
///
/// The specified value.
/// The absolute value of the parameter.
///
/// ```kalk
/// >>> abs(-1)
/// # abs(-1)
/// out = 1
/// >>> abs(float4(-1, 1, -2, -3))
/// # abs(float4(-1, 1, -2, -3))
/// out = float4(1, 1, 2, 3)
/// ```
///
[KalkExport("abs", CategoryMathFunctions, Functor = true)]
public object Abs(KalkCompositeValue x) => x.TransformArg(Engine, AbsFunc);
///
/// Returns a random value.
///
/// A value to create random values for.
/// A random value or a random value of the parameter.
///
/// ```kalk
/// >>> seed(0); rnd
/// # seed(0); rnd
/// out = 0.7262432699679598
/// >>> rnd
/// # rnd
/// out = 0.8173253595909687
/// >>> rnd(float4)
/// # rnd(float4)
/// out = float4(0.7680227, 0.5581612, 0.20603316, 0.5588848)
/// ```
///
[KalkExport("rnd", CategoryMathFunctions, Functor = true)]
public object Rnd(KalkCompositeValue x = null)
{
if (x != null)
{
return x.TransformArg(Engine, RndFunc);
}
else
{
return RndFunc(null);
}
}
///
/// Setup the seed function for rnd. The default seed is random.
///
/// An original seed value for the `rnd` function.
/// The x is not specified, it will generate a random seed automatically.
///
/// ```kalk
/// >>> seed(0); rnd
/// # seed(0); rnd
/// out = 0.7262432699679598
/// >>> seed(1); rnd
/// # seed(1); rnd
/// out = 0.24866858415709278
/// ```
///
[KalkExport("seed", CategoryMathFunctions)]
public void Seed(int? x = null)
{
_random = x.HasValue ? new Random(x.Value) : new Random();
}
///
/// Splits the value x into fractional and integer parts, each of which has the same sign as x.
///
/// The input value.
/// The signed-fractional portion of x.
///
/// ```kalk
/// >>> modf(1.5)
/// # modf(1.5)
/// out = [1, 0.5]
/// >>> modf(float2(-1.2, 3.4))
/// # modf(float2(-1.2, 3.4))
/// out = [float2(-1, 3), float2(-0.20000005, 0.4000001)]
/// ```
///
[KalkExport("modf", CategoryMathFunctions, Functor = true)]
public ScriptArray Modf(KalkCompositeValue x)
{
return new ScriptArray(2)
{
x.TransformArg(Engine, TruncFunc),
x.TransformArg(Engine, FracSignedFunc)
};
}
///
/// Converts the specified value from degrees to radians.
///
/// The specified value in degrees.
/// The x parameter converted from degrees to radians.
///
/// ```kalk
/// >>> radians(90)
/// # radians(90)
/// out = 1.5707963267948966
/// >>> radians(180)
/// # radians(180)
/// out = 3.141592653589793
/// ```
///
[KalkExport("radians", CategoryMathFunctions, Functor = true)]
public object Radians(KalkCompositeValue x) => x.TransformArg(Engine, RadiansFunc);
///
/// Converts the specified value from radians to degrees.
///
/// The x parameter converted from radians to degrees.
///
/// ```kalk
/// >>> degrees(pi/2)
/// # degrees(pi / 2)
/// out = 90
/// >>> degrees(pi)
/// # degrees(pi)
/// out = 180
/// ```
///
[KalkExport("degrees", CategoryMathFunctions, Functor = true)]
public object Degrees(KalkCompositeValue x) => x.TransformArg(Engine, DegreesFunc);
///
/// Returns an integer that indicates the sign of a number.
///
/// A signed number.
///
/// A number that indicates the sign of x:
/// - -1 if x is less than zero
/// - 0 if x is equal to zero
/// - 1 if x is greater than zero.
///
///
/// ```kalk
/// >>> sign(-5); sign(0); sign(2.3)
/// # sign(-5); sign(0); sign(2.3)
/// out = -1
/// out = 0
/// out = 1
/// >>> sign float4(-1, 2, 0, 1.5)
/// # sign(float4(-1, 2, 0, 1.5))
/// out = float4(-1, 1, 0, 1)
/// ```
///
[KalkExport("sign", CategoryMathFunctions, Functor = true)]
public object Sign(KalkCompositeValue x) => x.TransformArg(Engine, SignFunc);
///
/// Returns the cosine of the specified value.
///
/// The specified value, in radians.
/// The cosine of the x parameter.
///
/// ```kalk
/// >>> cos 0.5
/// # cos(0.5)
/// out = 0.8775825618903728
/// >>> cos float4(pi, pi/2, 0, 0.5)
/// # cos(float4(pi, pi / 2, 0, 0.5))
/// out = float4(-1, -4.371139E-08, 1, 0.87758255)
/// ```
///
[KalkExport("cos", CategoryMathFunctions, Functor = true)]
public object Cos(KalkDoubleValue x) => x.TransformArg(Engine, CosFunc);
///
/// Returns the arccosine of the specified value.
///
/// The specified value. Each component should be a floating-point value within the range of -1 to 1.
/// The arccosine of the x parameter.
///
/// ```kalk
/// >>> acos(-1)
/// # acos(-1)
/// out = 3.141592653589793
/// >>> acos(0)
/// # acos(0)
/// out = 1.5707963267948966
/// >>> acos(1)
/// # acos(1)
/// out = 0
/// >>> acos(float4(-1,0,1,0.5))
/// # acos(float4(-1, 0, 1, 0.5))
/// out = float4(3.1415927, 1.5707964, 0, 1.0471976)
/// ```
///
[KalkExport("acos", CategoryMathFunctions, Functor = true)]
public object Acos(KalkDoubleValue x) => x.TransformArg(Engine, AcosFunc);
///
/// Returns the hyperbolic cosine of the specified value.
///
/// The specified value, in radians.
/// The hyperbolic cosine of the x parameter.
///
/// ```kalk
/// >>> cosh(-1)
/// # cosh(-1)
/// out = 1.5430806348152437
/// >>> cosh(1)
/// # cosh(1)
/// out = 1.5430806348152437
/// >>> cosh(0)
/// # cosh(0)
/// out = 1
/// >>> cosh(float4(-1, 1, 0, 2))
/// # cosh(float4(-1, 1, 0, 2))
/// out = float4(1.5430807, 1.5430807, 1, 3.7621956)
/// ```
///
[KalkExport("cosh", CategoryMathFunctions, Functor = true)]
public object Cosh(KalkDoubleValue x) => x.TransformArg(Engine, CoshFunc);
///
/// Returns the inverse hyperbolic cosine of a number. The number must be greater than or equal to 1.
///
/// Any real number equal to or greater than 1.
/// The inverse hyperbolic cosine of the x parameter
///
/// ```kalk
/// >>> acosh(1)
/// # acosh(1)
/// out = 0
/// >>> acosh(10)
/// # acosh(10)
/// out = 2.993222846126381
/// >>> acosh(float4(1,2,4,10))
/// # acosh(float4(1, 2, 4, 10))
/// out = float4(0, 1.316958, 2.063437, 2.993223)
/// ```
///
[KalkExport("acosh", CategoryMathFunctions, Functor = true)]
public object Acosh(KalkDoubleValue x) => x.TransformArg(Engine, AcoshFunc);
///
/// Returns the sine of the specified value.
///
/// The specified value, in radians.
/// The sine of the x parameter.
///
/// ```kalk
/// >>> sin 0.5
/// # sin(0.5)
/// out = 0.479425538604203
/// >>> sin float4(pi, pi/2, 0, 0.5)
/// # sin(float4(pi, pi / 2, 0, 0.5))
/// out = float4(-8.742278E-08, 1, 0, 0.47942555)
/// ```
///
[KalkExport("sin", CategoryMathFunctions, Functor = true)]
public object Sin(KalkDoubleValue x) => x.TransformArg(Engine, SinFunc);
///
/// Returns the arcsine of the specified value.
///
/// The specified value. Each component of the x parameter should be within the range of -π/2 to π/2.
/// The arcsine of the x parameter.
///
/// ```kalk
/// >>> asin 0.5
/// # asin(0.5)
/// out = 0.5235987755982989
/// >>> asin float4(-1, 0, 1, 0.5)
/// # asin(float4(-1, 0, 1, 0.5))
/// out = float4(-1.5707964, 0, 1.5707964, 0.5235988)
/// ```
///
[KalkExport("asin", CategoryMathFunctions, Functor = true)]
public object Asin(KalkDoubleValue x) => x.TransformArg(Engine, AsinFunc);
///
/// Returns the hyperbolic sine of the specified value.
///
/// The specified value, in radians.
/// The hyperbolic sine of the x parameter.
///
/// ```kalk
/// >>> sinh(-1)
/// # sinh(-1)
/// out = -1.1752011936438014
/// >>> sinh(0)
/// # sinh(0)
/// out = 0
/// >>> sinh(1)
/// # sinh(1)
/// out = 1.1752011936438014
/// >>> sinh(float4(-1, 1, 0, 2))
/// # sinh(float4(-1, 1, 0, 2))
/// out = float4(-1.1752012, 1.1752012, 0, 3.6268604)
/// ```
///
[KalkExport("sinh", CategoryMathFunctions, Functor = true)]
public object Sinh(KalkDoubleValue x) => x.TransformArg(Engine, SinhFunc);
///
/// Returns the inverse hyperbolic sine of a number.
///
/// The specified value.
/// The inverse hyperbolic sine of the x parameter.
///
/// ```kalk
/// >>> asinh(-1.1752011936438014)
/// # asinh(-1.1752011936438014)
/// out = -1
/// >>> asinh(0)
/// # asinh(0)
/// out = 0
/// >>> asinh(1.1752011936438014)
/// # asinh(1.1752011936438014)
/// out = 1
/// >>> asinh(float4(-1.1752011936438014, 0, 1.1752011936438014, 2))
/// # asinh(float4(-1.1752011936438014, 0, 1.1752011936438014, 2))
/// out = float4(-1, 0, 1, 1.4436355)
/// ```
///
[KalkExport("asinh", CategoryMathFunctions, Functor = true)]
public object Asinh(KalkDoubleValue x) => x.TransformArg(Engine, AsinhFunc);
///
/// Returns the tangent of the specified value.
///
/// The specified value, in radians.
/// The tangent of the x parameter.
///
/// ```kalk
/// >>> tan(0.5)
/// # tan(0.5)
/// out = 0.5463024898437905
/// >>> tan(1)
/// # tan(1)
/// out = 1.5574077246549023
/// >>> tan float4(1, 2, 3, 4)
/// # tan(float4(1, 2, 3, 4))
/// out = float4(1.5574077, -2.1850398, -0.14254655, 1.1578213)
/// ```
///
[KalkExport("tan", CategoryMathFunctions, Functor = true)]
public object Tan(KalkDoubleValue x) => x.TransformArg(Engine, TanFunc);
///
/// Returns the arctangent of the specified value.
///
/// The specified value.
/// The arctangent of the x parameter. This value is within the range of -π/2 to π/2.
///
/// ```kalk
/// >>> atan(0.5)
/// # atan(0.5)
/// out = 0.4636476090008061
/// >>> atan(1)
/// # atan(1)
/// out = 0.7853981633974483
/// >>> atan(0)
/// # atan(0)
/// out = 0
/// >>> atan(float4(0,1,2,3))
/// # atan(float4(0, 1, 2, 3))
/// out = float4(0, 0.7853982, 1.1071488, 1.2490457)
/// ```
///
[KalkExport("atan", CategoryMathFunctions, Functor = true)]
public object Atan(KalkDoubleValue x) => x.TransformArg(Engine, AtanFunc);
///
/// Returns the hyperbolic tangent of the specified value.
///
/// The specified value, in radians.
/// The hyperbolic tangent of the x parameter.
///
/// ```kalk
/// >>> tanh(0)
/// # tanh(0)
/// out = 0
/// >>> tanh(1)
/// # tanh(1)
/// out = 0.7615941559557649
/// >>> tanh(2)
/// # tanh(2)
/// out = 0.9640275800758169
/// >>> tanh(float4(0, 1, 2, 3))
/// # tanh(float4(0, 1, 2, 3))
/// out = float4(0, 0.7615942, 0.9640276, 0.9950548)
/// ```
///
[KalkExport("tanh", CategoryMathFunctions, Functor = true)]
public object Tanh(KalkDoubleValue x) => x.TransformArg(Engine, TanhFunc);
///
/// Returns the inverse hyperbolic tangent of a number.
///
/// The specified value. Number must be between -1 and 1 (excluding -1 and 1).
/// The inverse hyperbolic tangent of the x parameter
///
/// ```kalk
/// >>> atanh(0)
/// # atanh(0)
/// out = 0
/// >>> atanh(0.5)
/// # atanh(0.5)
/// out = 0.5493061443340549
/// >>> atanh(float4(-0.5, 0, 0.5, 0.8))
/// # atanh(float4(-0.5, 0, 0.5, 0.8))
/// out = float4(-0.54930615, 0, 0.54930615, 1.0986123)
/// ```
///
[KalkExport("atanh", CategoryMathFunctions, Functor = true)]
public object Atanh(KalkDoubleValue x) => x.TransformArg(Engine, AtanhFunc);
///
/// Returns the arctangent of two values (x,y).
///
/// The y value.
/// The x value.
/// The arctangent of (y,x).
/// The signs of the x and y parameters are used to determine the quadrant of the return values within the range of -π to π. The `atan2` function is well-defined for every point other than the origin, even if y equals 0 and x does not equal 0.
///
/// ```kalk
/// >>> atan2(1,1)
/// # atan2(1, 1)
/// out = 0.7853981633974483
/// >>> atan2(1,0)
/// # atan2(1, 0)
/// out = 1.5707963267948966
/// >>> atan2(0,1)
/// # atan2(0, 1)
/// out = 0
/// >>> atan2(float4(1), float4(0,1,-1,2))
/// # atan2(float4(1), float4(0, 1, -1, 2))
/// out = float4(1.5707964, 0.7853982, 2.3561945, 0.4636476)
/// ```
///
[KalkExport("atan2", CategoryMathFunctions, Functor = true)]
public object Atan2(KalkDoubleValue y, KalkDoubleValue x)
{
var (xValues, yValues) = GetPairValues(x, y);
int index = 0;
return x.TransformArg(Engine, (double v) =>
{
var result = Math.Atan2(yValues[index], xValues[index]);
index++;
return result;
});
}
///
/// Returns the floating-point remainder of x/y.
///
/// The floating-point dividend.
/// The floating-point divisor.
/// The floating-point remainder of the x parameter divided by the y parameter.
/// The floating-point remainder is calculated such that x = i * y + f, where i is an integer, f has the same sign as x, and the absolute value of f is less than the absolute value of y.
///
/// ```kalk
/// >>> fmod(2.5, 2)
/// # fmod(2.5, 2)
/// out = 0.5
/// >>> fmod(2.5, 3)
/// # fmod(2.5, 3)
/// out = 2.5
/// >>> fmod(-1.5, 1)
/// # fmod(-1.5, 1)
/// out = -0.5
/// >>> fmod(float4(1.5, 1.2, -2.3, -4.6), 0.2)
/// # fmod(float4(1.5, 1.2, -2.3, -4.6), 0.2)
/// out = float4(0.09999998, 2.9802322E-08, -0.09999992, -0.19999984)
/// ```
///
[KalkExport("fmod", CategoryMathFunctions, Functor = true)]
public object Fmod(KalkDoubleValue x, KalkDoubleValue y)
{
var (xValues, yValues) = GetPairValues(x, y);
int index = 0;
return x.TransformArg(Engine, (double v) =>
{
var result = xValues[index] % yValues[index];
index++;
return result;
});
}
///
/// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
///
/// The specified value.
/// The fractional part of the x parameter.
///
/// ```kalk
/// >>> frac(1.25)
/// # frac(1.25)
/// out = 0.25
/// >>> frac(10.5)
/// # frac(10.5)
/// out = 0.5
/// >>> frac(-1.75)
/// # frac(-1.75)
/// out = 0.25
/// >>> frac(-10.25)
/// # frac(-10.25)
/// out = 0.75
/// >>> frac(float4(1.25, 10.5, -1.75, -10.25))
/// # frac(float4(1.25, 10.5, -1.75, -10.25))
/// out = float4(0.25, 0.5, 0.25, 0.75)
/// ```
///
[KalkExport("frac", CategoryMathFunctions, Functor = true)]
public object Frac(KalkDoubleValue x) => x.TransformArg(Engine, FracFunc);
///
/// Returns the reciprocal of the square root of the specified value.
///
/// The specified value.
/// The reciprocal of the square root of the x parameter.
/// This function uses the following formula: 1 / sqrt(x).
///
/// ```kalk
/// >>> rsqrt(1)
/// # rsqrt(1)
/// out = 1
/// >>> rsqrt(2)
/// # rsqrt(2)
/// out = 0.7071067811865475
/// >>> rsqrt(float4(1,2,3,4))
/// # rsqrt(float4(1, 2, 3, 4))
/// out = float4(1, 0.70710677, 0.57735026, 0.5)
/// ```
///
[KalkExport("rsqrt", CategoryMathFunctions, Functor = true)]
public object Rsqrt(KalkDoubleValue x) => x.TransformArg(Engine, RsqrtFunc);
///
/// Returns the square root of the specified floating-point value, per component.
///
/// The specified floating-point value.
/// The square root of the x parameter, per component.
///
/// ```kalk
/// >>> sqrt(1)
/// # sqrt(1)
/// out = 1
/// >>> sqrt(2)
/// # sqrt(2)
/// out = 1.4142135623730951
/// >>> sqrt(float4(1,2,3,4))
/// # sqrt(float4(1, 2, 3, 4))
/// out = float4(1, 1.4142135, 1.7320508, 2)
/// ```
///
[KalkExport("sqrt", CategoryMathFunctions, Functor = true)]
public object Sqrt(KalkDoubleValue x) => x.TransformArg(Engine, SqrtFunc);
///
/// Returns the base-e logarithm of the specified value.
///
/// The specified value.
/// The base-e logarithm of the x parameter. If the x parameter is negative, this function returns indefinite. If the x parameter is 0, this function returns `-inf`.
///
/// ```kalk
/// >>> log 1
/// # log(1)
/// out = 0
/// >>> log 2
/// # log(2)
/// out = 0.6931471805599453
/// >>> log 0
/// # log(0)
/// out = -inf
/// >>> log float4(0,1,2,3)
/// # log(float4(0, 1, 2, 3))
/// out = float4(-inf, 0, 0.6931472, 1.0986123)
/// ```
///
[KalkExport("log", CategoryMathFunctions, Functor = true)]
public object Log(KalkDoubleValue x) => x.TransformArg(Engine, LogFunc);
///
/// Returns the base-2 logarithm of the specified value.
///
/// The specified value.
/// The base-2 logarithm of the x parameter. If the x parameter is negative, this function returns indefinite. If the x parameter is 0, this function returns -inf.
///
/// ```kalk
/// >>> log2 0
/// # log2(0)
/// out = -inf
/// >>> log2 8
/// # log2(8)
/// out = 3
/// >>> log2 129
/// # log2(129)
/// out = 7.011227255423254
/// >>> log2 float4(0, 2, 16, 257)
/// # log2(float4(0, 2, 16, 257))
/// out = float4(-inf, 1, 4, 8.005625)
/// ```
///
[KalkExport("log2", CategoryMathFunctions, Functor = true)]
public object Log2(KalkDoubleValue x) => x.TransformArg(Engine, Log2Func);
///
/// Returns the base-10 logarithm of the specified value.
///
/// The specified value.
/// The base-10 logarithm of the x parameter. If the x parameter is negative, this function returns indefinite. If the x is 0, this function returns -inf.
///
/// ```kalk
/// >>> log10 0
/// # log10(0)
/// out = -inf
/// >>> log10 10
/// # log10(10)
/// out = 1
/// >>> log10 100
/// # log10(100)
/// out = 2
/// >>> log10 1001
/// # log10(1001)
/// out = 3.000434077479319
/// >>> log10(float4(0,10,100,1001))
/// # log10(float4(0, 10, 100, 1001))
/// out = float4(-inf, 1, 2, 3.0004342)
/// ```
///
[KalkExport("log10", CategoryMathFunctions, Functor = true)]
public object Log10(KalkDoubleValue x) => x.TransformArg(Engine, Log10Func);
///
/// Returns the base-e exponential, or e^x, of the specified value.
///
/// The specified value.
/// The base-e exponential of the x parameter.
///
/// ```kalk
/// >>> exp(0)
/// # exp(0)
/// out = 1
/// >>> exp(1)
/// # exp(1)
/// out = 2.718281828459045
/// >>> exp(float4(0,1,2,3))
/// # exp(float4(0, 1, 2, 3))
/// out = float4(1, 2.7182817, 7.389056, 20.085537)
/// ```
///
[KalkExport("exp", CategoryMathFunctions, Functor = true)]
public object Exp(KalkDoubleValue x) => x.TransformArg(Engine, ExpFunc);
///
/// Returns the base 2 exponential, or 2^x, of the specified value.
///
/// The specified value.
/// The base-2 exponential of the x parameter.
///
/// ```kalk
/// >>> exp2(0)
/// # exp2(0)
/// out = 1
/// >>> exp2(1)
/// # exp2(1)
/// out = 2
/// >>> exp2(4)
/// # exp2(4)
/// out = 16
/// >>> exp2(float4(0,1,2,3))
/// # exp2(float4(0, 1, 2, 3))
/// out = float4(1, 2, 4, 8)
/// ```
///
[KalkExport("exp2", CategoryMathFunctions, Functor = true)]
public object Exp2(KalkDoubleValue x) => x.TransformArg(Engine, Exp2Func);
///
/// Returns the specified value raised to the specified power.
///
/// The specified value.
/// The specified power.
/// The x parameter raised to the power of the y parameter.
///
/// ```kalk
/// >>> pow(1.5, 3.5)
/// # pow(1.5, 3.5)
/// out = 4.133513940946613
/// >>> pow(2, 4)
/// # pow(2, 4)
/// out = 16
/// >>> pow(float4(1,2,3,4), 4)
/// # pow(float4(1, 2, 3, 4), 4)
/// out = float4(1, 16, 81, 256)
/// >>> pow(float4(1..4), float4(5..8))
/// # pow(float4(1..4), float4(5..8))
/// out = float4(1, 64, 2187, 65536)
/// ```
///
[KalkExport("pow", CategoryMathFunctions, Functor = true)]
public object Pow(KalkDoubleValue x, KalkDoubleValue y)
{
var (xValues, yValues) = GetPairValues(x, y);
int index = 0;
return x.TransformArg(Engine, (double v) =>
{
var result = Math.Pow(xValues[index], yValues[index]);
index++;
return result;
});
}
///
/// Rounds the specified value to the nearest integer.
///
/// The specified value.
/// The x parameter, rounded to the nearest integer within a floating-point type.
///
/// ```kalk
/// >>> round(0.2); round(1.5); round(10.7)
/// # round(0.2); round(1.5); round(10.7)
/// out = 0
/// out = 2
/// out = 11
/// >>> round(-0.2); round(-1.5); round(-10.7)
/// # round(-0.2); round(-1.5); round(-10.7)
/// out = -0
/// out = -2
/// out = -11
/// ```
///
[KalkExport("round", CategoryMathFunctions, Functor = true)]
public object Round(KalkDoubleValue x) => x.TransformArg(Engine, RoundFunc);
///
/// Returns the largest integer that is less than or equal to the specified value.
///
/// The specified value.
/// The largest integer value (returned as a floating-point type) that is less than or equal to the x parameter.
///
/// ```kalk
/// >>> floor(0.2); floor(1.5); floor(10.7)
/// # floor(0.2); floor(1.5); floor(10.7)
/// out = 0
/// out = 1
/// out = 10
/// >>> floor(-0.2); floor(-1.5); floor(-10.7)
/// # floor(-0.2); floor(-1.5); floor(-10.7)
/// out = -1
/// out = -2
/// out = -11
/// ```
///
[KalkExport("floor", CategoryMathFunctions, Functor = true)]
public object Floor(KalkDoubleValue x) => x.TransformArg(Engine, FloorFunc);
///
/// Returns the smallest integer value that is greater than or equal to the specified value.
///
/// The specified input.
/// The smallest integer value (returned as a floating-point type) that is greater than or equal to the x parameter.
///
/// ```kalk
/// >>> ceil(0.2); ceil(1.5); ceil(10.7)
/// # ceil(0.2); ceil(1.5); ceil(10.7)
/// out = 1
/// out = 2
/// out = 11
/// >>> ceil(-0.2); ceil(-1.5); ceil(-10.7)
/// # ceil(-0.2); ceil(-1.5); ceil(-10.7)
/// out = -0
/// out = -1
/// out = -10
/// ```
///
[KalkExport("ceil", CategoryMathFunctions, Functor = true)]
public object Ceiling(KalkDoubleValue x) => x.TransformArg(Engine, CeilFunc);
///
/// Truncates a floating-point value to the integer component.
///
/// The specified input.
/// The input value truncated to an integer component.
/// This function truncates a floating-point value to the integer component. Given a floating-point value of 1.6, the trunc function would return 1.0, where as the round function would return 2.0.
///
/// ```kalk
/// >>> trunc(0.2); trunc(1.5); trunc(10.7)
/// # trunc(0.2); trunc(1.5); trunc(10.7)
/// out = 0
/// out = 1
/// out = 10
/// >>> trunc(-0.2); trunc(-1.5); trunc(-10.7)
/// # trunc(-0.2); trunc(-1.5); trunc(-10.7)
/// out = -0
/// out = -1
/// out = -10
/// ```
///
[KalkExport("trunc", CategoryMathFunctions, Functor = true)]
public object Trunc(KalkDoubleValue x) => x.TransformArg(Engine, TruncFunc);
///
/// Clamps the specified value within the range of 0 to 1.
///
/// The specified value.
/// The x parameter, clamped within the range of 0 to 1.
///
/// ```kalk
/// >>> saturate(10)
/// # saturate(10)
/// out = 1
/// >>> saturate(-10)
/// # saturate(-10)
/// out = 0
/// >>> saturate(float4(-1, 0.5, 1, 2))
/// # saturate(float4(-1, 0.5, 1, 2))
/// out = float4(0, 0.5, 1, 1)
/// ```
///
[KalkExport("saturate", CategoryMathFunctions, Functor = true)]
public object Saturate(KalkDoubleValue x) => x.TransformArg(Engine, SaturateFunc);
///
/// Selects the lesser of x and y.
///
/// The x input value.
/// The y input value.
/// The x or y parameter, whichever is the smallest value.
///
/// ```kalk
/// >>> min(-5, 6)
/// # min(-5, 6)
/// out = -5
/// >>> min(1, 0)
/// # min(1, 0)
/// out = 0
/// >>> min(float4(0, 1, 2, 3), float4(1, 0, 3, 2))
/// # min(float4(0, 1, 2, 3), float4(1, 0, 3, 2))
/// out = float4(0, 0, 2, 2)
/// ```
///
[KalkExport("min", CategoryMathFunctions, Functor = true)]
public object Min(KalkDoubleValue x, KalkDoubleValue y)
{
var (xValues,yValues) = GetPairValues(x,y);
int index = 0;
return x.TransformArg(Engine, (double v) =>
{
var result = Math.Min(xValues[index], yValues[index]);
index++;
return result;
});
}
///
/// Selects the greater of x and y.
///
/// The x input value.
/// The y input value.
/// The x or y parameter, whichever is the largest value.
///
/// ```kalk
/// >>> max(-5, 6)
/// # max(-5, 6)
/// out = 6
/// >>> max(1, 0)
/// # max(1, 0)
/// out = 1
/// >>> max(float4(0, 1, 2, 3), float4(1, 0, 3, 2))
/// # max(float4(0, 1, 2, 3), float4(1, 0, 3, 2))
/// out = float4(1, 1, 3, 3)
/// ```
///
[KalkExport("max", CategoryMathFunctions, Functor = true)]
public object Max(KalkDoubleValue x, KalkDoubleValue y)
{
var (xValues, yValues) = GetPairValues(x, y);
int index = 0;
return x.TransformArg(Engine, (double v) =>
{
var result = Math.Max(xValues[index], yValues[index]);
index++;
return result;
});
}
///
/// Compares two values, returning 0 or 1 based on which value is greater.
///
/// The first floating-point value to compare.
/// The second floating-point value to compare.
/// 1 if the x parameter is greater than or equal to the y parameter; otherwise, 0.
/// This function uses the following formula: (x >= y) ? 1 : 0. The function returns either 0 or 1 depending on whether the x parameter is greater than the y parameter. To compute a smooth interpolation between 0 and 1, use the `smoothstep` function.
///
/// ```kalk
/// >>> step(1, 5)
/// # step(1, 5)
/// out = 1
/// >>> step(5, 1)
/// # step(5, 1)
/// out = 0
/// >>> step(float4(0, 1, 2, 3), float4(1, 0, 3, 2))
/// # step(float4(0, 1, 2, 3), float4(1, 0, 3, 2))
/// out = float4(1, 0, 1, 0)
/// >>> step(-10, 5)
/// # step(-10, 5)
/// out = 1
/// >>> step(5.5, -10.5)
/// # step(5.5, -10.5)
/// out = 0
/// ```
///
[KalkExport("step", CategoryMathFunctions, Functor = true)]
public object Step(KalkDoubleValue y, KalkDoubleValue x)
{
var (xValues, yValues) = GetPairValues(x, y, nameof(x), nameof(y));
int index = 0;
return x.TransformArg(Engine, (double v) =>
{
var yv = yValues[index];
var xv = xValues[index];
index++;
return xv >= yv ? 1.0 : 0.0;
});
}
///
/// Returns a smooth Hermite interpolation between 0 and 1, if x is in the range [min, max].
///
/// The minimum range of the x parameter.
/// The maximum range of the x parameter.
/// The specified value to be interpolated.
/// Returns 0 if x is less than min; 1 if x is greater than max; otherwise, a value between 0 and 1 if x is in the range [min, max].
/// Use the smoothstep function to create a smooth transition between two values. For example, you can use this function to blend two colors smoothly.
///
/// ```kalk
/// >>> smoothstep(float4(0), float4(1), float4(-0.5))
/// # smoothstep(float4(0), float4(1), float4(-0.5))
/// out = float4(0, 0, 0, 0)
/// >>> smoothstep(float4(0), float4(1), float4(1.5))
/// # smoothstep(float4(0), float4(1), float4(1.5))
/// out = float4(1, 1, 1, 1)
/// >>> smoothstep(float4(0), float4(1), float4(0.5))
/// # smoothstep(float4(0), float4(1), float4(0.5))
/// out = float4(0.5, 0.5, 0.5, 0.5)
/// >>> smoothstep(float4(0), float4(1), float4(0.9))
/// # smoothstep(float4(0), float4(1), float4(0.9))
/// out = float4(0.972, 0.972, 0.972, 0.972)
/// ```
///
[KalkExport("smoothstep", CategoryMathFunctions, Functor = true)]
public object Smoothstep(KalkDoubleValue min, KalkDoubleValue max, KalkDoubleValue x)
{
var (xValues, minValues, maxValues) = GetTripleValues(x, min, max, nameof(x), nameof(min), nameof(max));
int index = 0;
return x.TransformArg(Engine, (double v) =>
{
var minValue = minValues[index];
var maxValue = maxValues[index];
index++;
if (v < minValue) v = 0.0;
if (v > maxValue) v = 1.0;
return v * v * (3.0f - (2.0f * v));
});
}
///
/// Performs a linear interpolation.
///
/// The first-floating point value.
/// The second-floating point value.
/// A value that linearly interpolates between the x parameter and the y parameter.
/// The result of the linear interpolation.
///
/// ```kalk
/// >>> lerp(0, 10, 0.5)
/// # lerp(0, 10, 0.5)
/// out = 5
/// >>> lerp(rgb("AliceBlue").xyz, rgb("Green").xyz, 0.5)
/// # lerp(rgb("AliceBlue").xyz, rgb("Green").xyz, 0.5)
/// out = float3(0.47058824, 0.7372549, 0.5)
/// ```
///
[KalkExport("lerp", CategoryMathFunctions, Functor = true)]
public object Lerp(KalkDoubleValue x, KalkDoubleValue y, KalkDoubleValue s)
{
var (xValues, yValues, sValues) = GetTripleValues(x, y, s, nameof(x), nameof(y), nameof(s));
int index = 0;
return x.TransformArg(Engine, (double xv) =>
{
var yv = yValues[index];
var sv = sValues[index];
index++;
return xv * (1 - sv) + yv * sv;
});
}
///
/// Clamps the specified value to the specified minimum and maximum range.
///
/// A value to clamp.
/// The specified minimum range.
/// The specified maximum range.
/// The clamped value for the x parameter.
/// For values of -inf or inf, clamp will behave as expected. However for values of `nan`, the results are undefined.
///
/// ```kalk
/// >>> clamp(-1, 0, 1)
/// # clamp(-1, 0, 1)
/// out = 0
/// >>> clamp(2, 0, 1)
/// # clamp(2, 0, 1)
/// out = 1
/// >>> clamp(0.5, 0, 1)
/// # clamp(0.5, 0, 1)
/// out = 0.5
/// >>> clamp(float4(0, 1, -2, 3), float4(0, -1, 3, 4), float4(1, 2, 5, 6))
/// # clamp(float4(0, 1, -2, 3), float4(0, -1, 3, 4), float4(1, 2, 5, 6))
/// out = float4(0, 1, 3, 4)
/// ```
///
[KalkExport("clamp", CategoryMathFunctions, Functor = true)]
public object Clamp(KalkDoubleValue x, KalkDoubleValue min, KalkDoubleValue max)
{
var (xValues, minValues, maxValues) = GetTripleValues(x, min, max, nameof(x), nameof(min), nameof(max));
int index = 0;
return x.TransformArg(Engine, (double xv) =>
{
var minv = minValues[index];
var maxv = maxValues[index];
index++;
return ClampImpl(xv, minv, maxv);
});
}
///
/// Returns the real part of the complex number.
///
/// A complex number.
/// The real part of the parameter x complex number.
///
/// ```kalk
/// >>> real(1.5 + 2.5i)
/// # real(1.5 + 2.5 * i)
/// out = 1.5
/// ```
///
[KalkExport("real", CategoryMathFunctions)]
public double Real(KalkComplex x) => x.Re;
///
/// Returns the imaginary part of the complex number.
///
/// A complex number.
/// The imaginary part of the parameter x complex number.
///
/// ```kalk
/// >>> imag(1.5 + 2.5i)
/// # imag(1.5 + 2.5 * i)
/// out = 2.5
/// ```
///
[KalkExport("imag", CategoryMathFunctions)]
public double Imag(KalkComplex x) => x.Im;
///
/// Returns the phase of the complex number.
///
/// A complex number.
/// The phase of the parameter x complex number.
///
/// ```kalk
/// >>> phase(1.5 + 2.5i)
/// # phase(1.5 + 2.5 * i)
/// out = 1.0303768265243125
/// ```
///
[KalkExport("phase", CategoryMathFunctions)]
public double Phase(KalkComplex x) => x.Phase;
///
/// Determines if the specified floating-point value is finite.
///
/// The specified value.
/// Returns a value of the same size as the input, with a value set to `true` if the x parameter is finite; otherwise `false`.
///
/// ```kalk
/// >>> isfinite(1)
/// # isfinite(1)
/// out = true
/// >>> isfinite(nan)
/// # isfinite(nan)
/// out = false
/// >>> isfinite(inf)
/// # isfinite(inf)
/// out = false
/// >>> isfinite(float4(1, -10.5, inf, nan))
/// # isfinite(float4(1, -10.5, inf, nan))
/// out = bool4(true, true, false, false)
/// ```
///
[KalkExport("isfinite", CategoryMathFunctions, Functor = true)]
public object IsFinite(KalkCompositeValue x) => x.TransformArg(Engine, IsFiniteFunc);
///
/// Determines if the specified value is infinite.
///
/// The specified value.
/// Returns a value of the same size as the input, with a value set to `true` if the x parameter is +inf or -inf. Otherwise, `false`.
///
/// ```kalk
/// >>> isinf(1)
/// # isinf(1)
/// out = false
/// >>> isinf(inf)
/// # isinf(inf)
/// out = true
/// >>> isinf(float4(1, -10.5, inf, nan))
/// # isinf(float4(1, -10.5, inf, nan))
/// out = bool4(false, false, true, false)
/// ```
///
[KalkExport("isinf", CategoryMathFunctions, Functor = true)]
public object IsInf(KalkCompositeValue x) => x.TransformArg(Engine, IsInfFunc);
///
/// Determines if the specified value is `nan`.
///
/// The specified value.
/// Returns a value of the same size as the input, with a value set to `true` if the x parameter is `nan`. Otherwise, `false`.
///
/// ```kalk
/// >>> isnan(1)
/// # isnan(1)
/// out = false
/// >>> isnan(inf)
/// # isnan(inf)
/// out = false
/// >>> isnan(nan)
/// # isnan(nan)
/// out = true
/// >>> isnan(float4(1, -10.5, inf, nan))
/// # isnan(float4(1, -10.5, inf, nan))
/// out = bool4(false, false, false, true)
/// ```
///
[KalkExport("isnan", CategoryMathFunctions, Functor = true)]
public object IsNan(KalkCompositeValue x) => x.TransformArg(Engine, IsNanFunc);
///
/// Performs the summation of the specified value.
///
/// The specified value.
/// Additional values.
/// The summation of the values.
///
/// ```kalk
/// >>> sum(1,2,3,4)
/// # sum(1, 2, 3, 4)
/// out = 10
/// >>> sum(float4(1..4))
/// # sum(float4(1..4))
/// out = 10
/// >>> sum(float4(1..4), float4(5..8))
/// # sum(float4(1..4), float4(5..8))
/// out = float4(15, 16, 17, 18)
/// >>> sum("a", "b", "c")
/// # sum("a", "b", "c")
/// out = "abc"
/// >>> sum(["a", "b", "c"])
/// # sum(["a", "b", "c"])
/// out = "abc"
/// ```
///
[KalkExport("sum", CategoryMathFunctions)]
public object Sum(object value, params object[] values)
{
if (value == null) throw new ArgumentNullException(nameof(value));
object result = value;
if (value is IEnumerable it)
{
bool resetFirst = true;
foreach (var nextValue in it)
{
if (resetFirst)
{
result = nextValue;
resetFirst = false;
}
else
{
result = ScriptBinaryExpression.Evaluate(Engine, Engine.CurrentSpan, ScriptBinaryOperator.Add, result, nextValue);
}
}
}
foreach (var nextValue in values)
{
result = ScriptBinaryExpression.Evaluate(Engine, Engine.CurrentSpan, ScriptBinaryOperator.Add, result, nextValue);
}
return result;
}
private (List, List) GetPairValues(KalkDoubleValue x, KalkDoubleValue y, string nameofx = "x", string nameofy = "y")
{
var xValues = new List();
var yValues = new List();
x.TransformArg(Engine, (double v) =>
{
xValues.Add(v);
return v;
});
y = Cast(x, y, nameofy);
y.TransformArg(Engine, (double v) =>
{
yValues.Add(v);
return v;
});
if (xValues.Count != yValues.Count)
{
throw new ArgumentException($"Invalid length between {nameofx} with {xValues.Count} elements and {nameofy} with {yValues.Count} elements.", nameofx);
}
return (xValues, yValues);
}
///
/// Cast y to x type.
///
private KalkDoubleValue Cast(KalkDoubleValue x, KalkDoubleValue y, string nameofy = "y")
{
if (x.Transformable != null && y.Transformable == null)
{
try
{
var ty = Engine.ToObject(1, y.Value);
return new KalkDoubleValue((IScriptTransformable) x.TransformArg(Engine, (double v) => ty));
}
catch
{
throw new ArgumentException($"Error converting {nameofy} to {Engine.GetTypeName(x)}.", nameofy);
}
}
return y;
}
private (List, List, List) GetTripleValues(KalkDoubleValue x, KalkDoubleValue y, KalkDoubleValue z, string nameofx = "x", string nameofy = "y", string nameofz = "z")
{
var xValues = new List();
var yValues = new List();
var zValues = new List();
x.TransformArg(Engine, (double v) =>
{
xValues.Add(v);
return v;
});
y = Cast(x, y, nameofy);
y.TransformArg(Engine, (double v) =>
{
yValues.Add(v);
return v;
});
z = Cast(x, z, nameofz);
z.TransformArg(Engine, (double v) =>
{
zValues.Add(v);
return v;
});
if (xValues.Count != yValues.Count || xValues.Count != zValues.Count)
{
throw new ArgumentException($"Invalid length between {nameofx} with {xValues.Count} elements, {nameofy} with {yValues.Count} elements and {nameofz} with {zValues.Count} elements.", nameofx);
}
return (xValues, yValues, zValues);
}
private static double FracImpl(double x)
{
if (x < 0)
{
return 1.0 + x + Math.Truncate(-x);
}
return x - Math.Truncate(x);
}
private static double FracSignedImpl(double x)
{
return x - Math.Truncate(x);
}
private static KalkBool IsFiniteFuncImpl(object arg)
{
if (arg is float f32) return float.IsFinite(f32);
if (arg is double f64) return double.IsFinite(f64);
return true;
}
private static KalkBool IsInfFuncImpl(object arg)
{
if (arg is float f32) return float.IsInfinity(f32);
if (arg is double f64) return double.IsInfinity(f64);
return false;
}
private static KalkBool IsNanFuncImpl(object arg)
{
if (arg is float f32) return float.IsNaN(f32);
if (arg is double f64) return double.IsNaN(f64);
return false;
}
private object SignFuncImpl(object value)
{
if (value == null) return null;
var type = value.GetType();
if (type == typeof(int))
{
return Math.Sign((int)value);
}
if (type == typeof(float))
{
return Math.Sign((float)value);
}
if (type == typeof(double))
{
return Math.Sign((double)value);
}
if (type == typeof(long))
{
return Math.Sign((long)value);
}
if (type == typeof(decimal))
{
return Math.Sign((decimal)value);
}
if (type == typeof(BigInteger))
{
return ((BigInteger)value).Sign;
}
return Math.Sign(Engine.ToObject(0, value));
}
private BigInteger Fibonacci(int value)
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "The value must be > 0");
if (value == 0) return BigInteger.Zero;
if (value == 1) return BigInteger.One;
var fn = BigInteger.Zero;
var fn1 = BigInteger.One;
var n = (uint) value;
for (uint bit = (0x80000000 >> BitOperations.LeadingZeroCount(n)); bit != 0; bit >>= 1)
{
// F(2n) = F(n) * (2*F(n+1) - F(n))
// F(2n+1) = F(n+1)^2 + F(n)^2
var f2n = fn * ((fn1 << 1) - fn);
var f2n1 = fn1 * fn1 + fn * fn;
fn = f2n;
fn1 = f2n1;
if ((n & bit) != 0)
{
var nfn1 = fn + fn1;
fn = fn1;
fn1 = nfn1;
}
}
return fn;
}
private object RndImpl(object value = null)
{
if (value == null) return _random.NextDouble();
var type = value.GetType();
if (type == typeof(int))
{
return _random.NextFullRangeInt32();
}
if (type == typeof(float))
{
return (float)_random.NextDouble();
}
if (type == typeof(double))
{
return _random.NextDouble();
}
if (type == typeof(long))
{
return _random.NextFullRangeInt64();
}
if (type == typeof(decimal))
{
return _random.NextDecimal();
}
if (type == typeof(BigInteger))
{
return (BigInteger)_random.NextFullRangeInt64();
}
return _random.NextDouble();
}
private object AbsImpl(object value)
{
if (value == null) return null;
var type = value.GetType();
if (type == typeof(int))
{
return Math.Abs((int) value);
}
if (type == typeof(float))
{
return Math.Abs((float)value);
}
if (type == typeof(double))
{
return Math.Abs((double)value);
}
if (type == typeof(long))
{
return Math.Abs((long)value);
}
if (type == typeof(decimal))
{
return Math.Abs((decimal)value);
}
if (type == typeof(BigInteger))
{
return BigInteger.Abs((BigInteger)value);
}
return Math.Abs(Engine.ToObject(0, value));
}
public static double SaturateImpl(double x) => x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
public static double ClampImpl(double x, double min, double max) => Math.Min(Math.Max(x, min), max);
}
}