Fix and cover standard library functions
This commit is contained in:
parent
a936188e46
commit
858717deae
11 changed files with 146 additions and 30 deletions
|
|
@ -136,14 +136,14 @@ decimal angle = interpreter.Interpret("degrees(pi / 2)");
|
|||
|
||||
The standard library includes:
|
||||
|
||||
- Base functions: `abs`, `sign`, `min`, `max`
|
||||
- Base functions: `abs`, `sign`, `min`, `max`, `sqrt`, `pow`, `log`, `ln`, `lerp`, `smoothstep`, `map`, `normalize`
|
||||
- Angle helpers: `degrees`, `radians`, `wrapangle`
|
||||
- Rounding helpers: `floor`, `ceiling`, `truncate`, `frac`, `round`, `clamp`
|
||||
- Trigonometry: `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`
|
||||
- Hyperbolic trigonometry: `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`
|
||||
- Number theory: `fac`, `ncr`, `npr`, `gcd`, `lcm`
|
||||
- Number theory: `fac`, `ncr`, `npr`, `gcd`, `lcm`, `fib`, `iseven`, `isodd`, `isint`, `isprime`
|
||||
- Random helpers: `flip`, `bern`, `rand`, `rands`, `randn`, `randns`
|
||||
- Constants: `pi`, `e`, `tau`, `phi`, `goldenratio`, `eurler`, `omega`
|
||||
- Constants: `pi`, `e`, `tau`, `phi`, `goldenratio`, `eurler`, `euler`, `omega`
|
||||
|
||||
## Custom Functions
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ namespace CSMic.StandardLibrary.Functions
|
|||
var input2 = _args[1].Value;
|
||||
var input3 = _args[2].Value;
|
||||
|
||||
decimal start = Convert.ToDecimal(input);
|
||||
decimal end = Convert.ToDecimal(input2);
|
||||
decimal ammount = Convert.ToDecimal(input3);
|
||||
decimal start = Convert.ToDecimal(input.Value);
|
||||
decimal end = Convert.ToDecimal(input2.Value);
|
||||
decimal ammount = Convert.ToDecimal(input3.Value);
|
||||
|
||||
if (start == end)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ namespace CSMic.StandardLibrary.Functions
|
|||
var input4 = _args[3].Value;
|
||||
var input5 = _args[4].Value;
|
||||
|
||||
decimal number = Convert.ToDecimal(input);
|
||||
decimal oldMinimum = Convert.ToDecimal(input2);
|
||||
decimal oldMaximum = Convert.ToDecimal(input3);
|
||||
decimal newMinimum = Convert.ToDecimal(input4);
|
||||
decimal newMaximum = Convert.ToDecimal(input5);
|
||||
decimal number = Convert.ToDecimal(input.Value);
|
||||
decimal oldMinimum = Convert.ToDecimal(input2.Value);
|
||||
decimal oldMaximum = Convert.ToDecimal(input3.Value);
|
||||
decimal newMinimum = Convert.ToDecimal(input4.Value);
|
||||
decimal newMaximum = Convert.ToDecimal(input5.Value);
|
||||
|
||||
if (oldMinimum == oldMaximum)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ namespace CSMic.StandardLibrary.Functions
|
|||
var input2 = _args[1].Value;
|
||||
var input3 = _args[2].Value;
|
||||
|
||||
decimal number = Convert.ToDecimal(input);
|
||||
decimal minimum = Convert.ToDecimal(input2);
|
||||
decimal maximum = Convert.ToDecimal(input3);
|
||||
decimal number = Convert.ToDecimal(input.Value);
|
||||
decimal minimum = Convert.ToDecimal(input2.Value);
|
||||
decimal maximum = Convert.ToDecimal(input3.Value);
|
||||
|
||||
if (minimum == maximum)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ namespace CSMic.StandardLibrary.Functions.NumberTheory
|
|||
{
|
||||
var input = _args[0].Value;
|
||||
|
||||
int index = Convert.ToInt32(input);
|
||||
int index = Convert.ToInt32(input.Value);
|
||||
|
||||
if(index <= 0 || index > 140)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ namespace CSMic.StandardLibrary.Functions.NumberTheory
|
|||
{
|
||||
var input = _args[0].Value;
|
||||
|
||||
decimal value = Convert.ToDecimal(input);
|
||||
decimal value = Convert.ToDecimal(input.Value);
|
||||
|
||||
return new FunctionValue(FunctionValueType.Numeric, IsEven.CalculateIsEven(value) ? 1m : 0m);
|
||||
});
|
||||
|
|
@ -61,7 +61,7 @@ namespace CSMic.StandardLibrary.Functions.NumberTheory
|
|||
|
||||
internal static bool CalculateIsEven(decimal value)
|
||||
{
|
||||
return true;
|
||||
return IsInt.CalculateIsInt(value) && value % 2m == 0m;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,11 +113,16 @@ namespace CSMic.StandardLibrary.Functions.NumberTheory
|
|||
{
|
||||
var input = _args[0].Value;
|
||||
|
||||
decimal value = Convert.ToDecimal(input);
|
||||
decimal value = Convert.ToDecimal(input.Value);
|
||||
|
||||
return new FunctionValue(FunctionValueType.Numeric, IsEven.CalculateIsEven(value) ? 0m : 1m);
|
||||
return new FunctionValue(FunctionValueType.Numeric, CalculateIsOdd(value) ? 1m : 0m);
|
||||
});
|
||||
}
|
||||
|
||||
internal static bool CalculateIsOdd(decimal value)
|
||||
{
|
||||
return IsInt.CalculateIsInt(value) && value % 2m != 0m;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -169,7 +174,7 @@ namespace CSMic.StandardLibrary.Functions.NumberTheory
|
|||
{
|
||||
var input = _args[0].Value;
|
||||
|
||||
decimal value = Convert.ToDecimal(input);
|
||||
decimal value = Convert.ToDecimal(input.Value);
|
||||
|
||||
return new FunctionValue(FunctionValueType.Numeric, CalculateIsInt(value) ? 1m : 0m);
|
||||
});
|
||||
|
|
@ -255,7 +260,7 @@ namespace CSMic.StandardLibrary.Functions.NumberTheory
|
|||
{
|
||||
var input = _args[0].Value;
|
||||
|
||||
decimal value = Convert.ToDecimal(input);
|
||||
decimal value = Convert.ToDecimal(input.Value);
|
||||
|
||||
return new FunctionValue(FunctionValueType.Numeric, CalculateIsPrime(value) ? 1m : 0m);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using System.Text;
|
|||
namespace CSMic.StandardLibrary.Functions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the standard-library <c>lerp</c> smooth-step function.
|
||||
/// Represents the standard-library <c>smoothstep</c> function.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function evaluates a value between two edges, clamps it to the range from <c>0</c> through <c>1</c>, and
|
||||
|
|
@ -16,17 +16,17 @@ namespace CSMic.StandardLibrary.Functions
|
|||
/// <summary>
|
||||
/// Gets the expression-language name used to invoke this function.
|
||||
/// </summary>
|
||||
/// <value><c>lerp</c>.</value>
|
||||
/// <value><c>smoothstep</c>.</value>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "lerp";
|
||||
return "smoothstep";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the argument signature expected by the <c>lerp</c> smooth-step function.
|
||||
/// Gets the argument signature expected by the <c>smoothstep</c> function.
|
||||
/// </summary>
|
||||
/// <value>Three numeric arguments named <c>startEdge</c>, <c>endEdge</c>, and <c>value</c>.</value>
|
||||
public override IEnumerable<FunctionArgument> ExpectedArguments
|
||||
|
|
@ -40,7 +40,7 @@ namespace CSMic.StandardLibrary.Functions
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the <c>lerp</c> smooth-step function.
|
||||
/// Executes the <c>smoothstep</c> function.
|
||||
/// </summary>
|
||||
/// <param name="args">
|
||||
/// The evaluated arguments supplied to the function. Exactly three numeric arguments are expected.
|
||||
|
|
@ -56,9 +56,9 @@ namespace CSMic.StandardLibrary.Functions
|
|||
var input2 = _args[1].Value;
|
||||
var input3 = _args[2].Value;
|
||||
|
||||
decimal startEdge = Convert.ToDecimal(input);
|
||||
decimal endEdge = Convert.ToDecimal(input2);
|
||||
decimal value = Convert.ToDecimal(input3);
|
||||
decimal startEdge = Convert.ToDecimal(input.Value);
|
||||
decimal endEdge = Convert.ToDecimal(input2.Value);
|
||||
decimal value = Convert.ToDecimal(input3.Value);
|
||||
|
||||
var normalization = Math.Clamp((value - startEdge) / (endEdge - startEdge), 0, 1);
|
||||
var polynomialization = normalization * normalization * (3 - (2 * normalization));
|
||||
|
|
|
|||
|
|
@ -65,6 +65,13 @@ namespace CSMic.StandardLibrary
|
|||
inputInterpreter.RegisterFunction(new Min());
|
||||
inputInterpreter.RegisterFunction(new Max());
|
||||
inputInterpreter.RegisterFunction(new SquareRoot());
|
||||
inputInterpreter.RegisterFunction(new Power());
|
||||
inputInterpreter.RegisterFunction(new Log());
|
||||
inputInterpreter.RegisterFunction(new Natural_Log());
|
||||
inputInterpreter.RegisterFunction(new Lerp());
|
||||
inputInterpreter.RegisterFunction(new SmoothStep());
|
||||
inputInterpreter.RegisterFunction(new Map());
|
||||
inputInterpreter.RegisterFunction(new Normalize());
|
||||
}
|
||||
|
||||
/// <summary> Initializes the angle-related functions. </summary>
|
||||
|
|
@ -97,6 +104,11 @@ namespace CSMic.StandardLibrary
|
|||
inputInterpreter.RegisterFunction(new Permutations());
|
||||
inputInterpreter.RegisterFunction(new GreatestCommonDivisor());
|
||||
inputInterpreter.RegisterFunction(new LeastCommonMultiple());
|
||||
inputInterpreter.RegisterFunction(new Fibonacci());
|
||||
inputInterpreter.RegisterFunction(new IsEven());
|
||||
inputInterpreter.RegisterFunction(new IsOdd());
|
||||
inputInterpreter.RegisterFunction(new IsInt());
|
||||
inputInterpreter.RegisterFunction(new IsPrime());
|
||||
}
|
||||
|
||||
/// <summary> Initializes the rounding functions. </summary>
|
||||
|
|
@ -178,6 +190,7 @@ namespace CSMic.StandardLibrary
|
|||
inputInterpreter.Interpret("tau :: 6.2831853071795862");
|
||||
inputInterpreter.Interpret("phi :: 1.6180339887498948");
|
||||
inputInterpreter.Interpret("goldenratio :: 1.6180339887498948");
|
||||
inputInterpreter.Interpret("eurler :: 0.5772156649015329");
|
||||
inputInterpreter.Interpret("euler :: 0.5772156649015329");
|
||||
inputInterpreter.Interpret("omega :: 0.5671432904097839");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ decimal result = interpreter.Interpret("max(10, abs(-12))");
|
|||
- `phi`
|
||||
- `goldenratio`
|
||||
- `eurler`
|
||||
- `euler`
|
||||
- `omega`
|
||||
|
||||
Constants are stored as interpreter variables, so they can be used in normal expressions:
|
||||
|
|
@ -59,6 +60,14 @@ Base functions:
|
|||
- `sign(value)`
|
||||
- `min(left, right)`
|
||||
- `max(left, right)`
|
||||
- `sqrt(value)`
|
||||
- `pow(base, exponent)`
|
||||
- `log(value, base)`
|
||||
- `ln(value)`
|
||||
- `lerp(start, end, amount)`
|
||||
- `smoothstep(startEdge, endEdge, value)`
|
||||
- `map(value, oldMinimum, oldMaximum, newMinimum, newMaximum)`
|
||||
- `normalize(value, minimum, maximum)`
|
||||
|
||||
Angle functions:
|
||||
|
||||
|
|
@ -101,6 +110,11 @@ Number theory functions:
|
|||
- `npr(n, r)`
|
||||
- `gcd(left, right)`
|
||||
- `lcm(left, right)`
|
||||
- `fib(index)`
|
||||
- `iseven(value)`
|
||||
- `isodd(value)`
|
||||
- `isint(value)`
|
||||
- `isprime(value)`
|
||||
|
||||
Random functions:
|
||||
|
||||
|
|
|
|||
|
|
@ -80,5 +80,35 @@ public class NumberTheoryFunctionsTests
|
|||
// Gamma(1.5) ~= sqrt(pi)/2 ~= 0.8862269254527579
|
||||
AssertApprox(res, 0.8862269254527579m, 0.0000000000001m, _interp);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Fibonacci_Works_And_Validates()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("fib(1)"), 1m, _interp);
|
||||
AssertSuccess(_interp.Interpret("fib(2)"), 1m, _interp);
|
||||
AssertSuccess(_interp.Interpret("fib(10)"), 55m, _interp);
|
||||
AssertSuccess(_interp.Interpret("fib(0)"), 0m, _interp);
|
||||
AssertSuccess(_interp.Interpret("fib(-1)"), 0m, _interp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IdentityFunctions_Work()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("iseven(2)"), 1m, _interp);
|
||||
AssertSuccess(_interp.Interpret("iseven(3)"), 0m, _interp);
|
||||
AssertSuccess(_interp.Interpret("iseven(2.5)"), 0m, _interp);
|
||||
|
||||
AssertSuccess(_interp.Interpret("isodd(3)"), 1m, _interp);
|
||||
AssertSuccess(_interp.Interpret("isodd(2)"), 0m, _interp);
|
||||
AssertSuccess(_interp.Interpret("isodd(2.5)"), 0m, _interp);
|
||||
|
||||
AssertSuccess(_interp.Interpret("isint(2)"), 1m, _interp);
|
||||
AssertSuccess(_interp.Interpret("isint(2.5)"), 0m, _interp);
|
||||
|
||||
AssertSuccess(_interp.Interpret("isprime(2)"), 1m, _interp);
|
||||
AssertSuccess(_interp.Interpret("isprime(97)"), 1m, _interp);
|
||||
AssertSuccess(_interp.Interpret("isprime(1)"), 0m, _interp);
|
||||
AssertSuccess(_interp.Interpret("isprime(9)"), 0m, _interp);
|
||||
AssertSuccess(_interp.Interpret("isprime(2.5)"), 0m, _interp);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ public class StdlibFunctionsTests
|
|||
Assert.That(interp.StringValue, Is.EqualTo(string.Empty));
|
||||
}
|
||||
|
||||
private static void AssertApprox(decimal result, decimal expected, decimal tol, InputInterpreter interp)
|
||||
{
|
||||
Assert.That(Math.Abs(result - expected) <= tol, $"Expected ~{expected} +/- {tol}, got {result}");
|
||||
Assert.That(interp.StringValue, Is.EqualTo(string.Empty));
|
||||
}
|
||||
|
||||
[TestCase("abs(-1)", 1)]
|
||||
[TestCase("abs(0)", 0)]
|
||||
[TestCase("abs(2)", 2)]
|
||||
|
|
@ -98,12 +104,60 @@ public class StdlibFunctionsTests
|
|||
AssertSuccess(result, expected, _interp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Power_Works()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("pow(2, 3)"), 8m, _interp);
|
||||
AssertApprox(_interp.Interpret("pow(9, 0.5)"), 3m, 0.0000000000001m, _interp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Logarithms_Work()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("log(8, 2)"), 3m, _interp);
|
||||
AssertApprox(_interp.Interpret("ln(e)"), 1m, 0.0000000000001m, _interp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Lerp_Works()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("lerp(10, 20, 0)"), 10m, _interp);
|
||||
AssertSuccess(_interp.Interpret("lerp(10, 20, 0.25)"), 12.5m, _interp);
|
||||
AssertSuccess(_interp.Interpret("lerp(10, 20, 1)"), 20m, _interp);
|
||||
AssertSuccess(_interp.Interpret("lerp(10, 10, 0.5)"), 10m, _interp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SmoothStep_Works()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("smoothstep(0, 1, -1)"), 0m, _interp);
|
||||
AssertSuccess(_interp.Interpret("smoothstep(0, 1, 0.5)"), 0.5m, _interp);
|
||||
AssertSuccess(_interp.Interpret("smoothstep(0, 1, 2)"), 1m, _interp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_Works_And_HandlesEmptySourceRange()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("map(5, 0, 10, 0, 100)"), 50m, _interp);
|
||||
AssertSuccess(_interp.Interpret("map(15, 10, 20, 100, 200)"), 150m, _interp);
|
||||
AssertSuccess(_interp.Interpret("map(5, 1, 1, 0, 100)"), 0m, _interp);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Normalize_Works_And_HandlesEmptyRange()
|
||||
{
|
||||
AssertSuccess(_interp.Interpret("normalize(5, 0, 10)"), 0.5m, _interp);
|
||||
AssertSuccess(_interp.Interpret("normalize(15, 10, 20)"), 0.5m, _interp);
|
||||
AssertSuccess(_interp.Interpret("normalize(5, 1, 1)"), 0m, _interp);
|
||||
}
|
||||
|
||||
[TestCase("pi", "3.1415926535897931")]
|
||||
[TestCase("e", "2.7182818284590451")]
|
||||
[TestCase("tau", "6.2831853071795862")]
|
||||
[TestCase("phi", "1.6180339887498948")]
|
||||
[TestCase("goldenratio", "1.6180339887498948")]
|
||||
[TestCase("eurler", "0.5772156649015329")]
|
||||
[TestCase("euler", "0.5772156649015329")]
|
||||
[TestCase("omega", "0.5671432904097839")]
|
||||
public void Constants_AreAvailable(string expr, string expectedStr)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue