NoStringEvaluating
Fast low memory consuming mathematical evaluation without endless string parsing! Parses string formula once and uses its object sequence in each evaluation. Moreover, provides user defined functions and variables.
Install / Use
/learn @KovtunV/NoStringEvaluatingREADME
Fast low memory consuming mathematical evaluation without endless string parsing! Parses string formula once and uses its object sequence in each evaluation. Moreover provides user defined functions and variables. <br/>
<br/>
Microsoft.Extensions.DependencyInjection <br/>
<br/>
Quick Links
<!--ts-->- Features
- Performance
- Quick start
- Extra types
- Variables
- Operators
- Boolean operators
- Functions
- Options
- Documentation
- Thanks for contribution :octocat:
- TODO
Features
- Fast math evaluation
- Zero-allocation code (object pooling)
- User defined functions
- User defined variables with any chars
- Mixed result type
- Scientific notation
Performance
Compared with a good solution mXparser
- In general, x5 faster!
Testing formulas
| № | Formula | | ------------ | ------------ | | Empty | | | NumberOnly | 3 | | 1 | 3 * 9 | | 2 | 3 * 9 / 456 * 32 + 12 / 17 - 3 | | 3 | 3 * (9 / 456 * (32 + 12)) / 17 - 3 | | 4 | (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) - (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) + (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) * 345 * ((897 - 323)/ 23) | | 5 | Arg1 * Arg2 + Arg3 - Arg4 | | 6 | Arg1 * (Arg2 + Arg3) - Arg4 / (Arg5 - Arg6 + 1) + 45 * Arg7 + ((Arg8 * 56 + (12 + Arg9))) - Arg10 | | 7 | add(1; 2; 3) | | 8 | add(add(5; 1) - add(5; 2; 3)) | | 9 | if(Arg1 > 0; add(56 + 9 / 12 * 123.596; or(78; 9; 5; 2; 4; 5; 8; 7); 45;5); 9) * 24 + 52 -33 | | 10 | kov(1; 2; 3) - kovt(8; 9) |
1 000 000 calculations
Less is better

Benchmark results

Conclusion
As you can see this solution is faster in all cases, furthermore there isn't any garbage collection.
Benchmark code - src/ConsoleApp/Benchmark/BenchNumbers.cs
Benchmark excel - benchResults/Benchmark.xlsx
Quick start
Initialization
There are two ways to use evaluator:
- Static initialization
public void SomeMethod()
{
var facade = NoStringEvaluator.CreateFacade();
var evaluator = facade.Evaluator;
}
- DI from the package
public void ConfigureServices(IServiceCollection services)
{
// ......
services.AddNoStringEvaluator();
}
Usage
Add INoStringEvaluator to your controller, service, etc...
And just send string or FormulaNodes to evaluation:
public class MyService
{
private INoStringEvaluator _noStringEvaluator;
public MyService(INoStringEvaluator noStringEvaluator)
{
_noStringEvaluator = noStringEvaluator;
}
public double CalcNumber(string formula)
{
return _noStringEvaluator.CalcNumber(formula);
}
public string CalcWord(string formula)
{
return _noStringEvaluator.CalcWord(formula);
}
public EvaluatorValue Calc(string formula)
{
return _noStringEvaluator.Calc(formula);
}
}
If you have variables, you can send IDictionary or your IVariablesContainer implementation:
public class MyService
{
private INoStringEvaluator _noStringEvaluator;
public MyService(INoStringEvaluator noStringEvaluator)
{
_noStringEvaluator = noStringEvaluator;
}
public double Calc(string formula, IDictionary<string, EvaluatorValue> variables)
{
return _noStringEvaluator.CalcNumber(formula, variables);
}
}
User defined functions
If you need your function, just implement the interface IFunction If you want to returnt extra type, use factory.
As an argument's separator can be:
- ;
- ,
For instance, usage function "YouAre('Vitaly'; 26)":
public class MyFunction : IFunction
{
public string Name { get; } = "YouAre";
public bool CanHandleNullArguments { get; }
public InternalEvaluatorValue Execute(List<InternalEvaluatorValue> args, ValueFactory factory)
{
var name = args[0].Word;
var age = args[1];
var ageAfterDecade = age + 10;
var result = $"Hello, {name}. After 10 years you will be {ageAfterDecade} y.o.";
return factory.Word.Create(result);
}
}
And don't forget to initialize your functions via options or directly in IFunctionReader
public void SomeMethod()
{
// NoStringEvaluator.CreateFacade(opt => opt.WithFunctionsFrom(<type from source assembly>));
// NoStringEvaluator.CreateFacade(opt => opt.WithFunctionsFrom(<source assembly>));
// NoStringEvaluator.CreateFacade(opt => opt.WithFunctions(new MyFunction()));
// same with DI
// services.AddNoStringEvaluator(opt => opt.WithFunctions(new MyFunction()));
}
Extra types
Apart from double calculations you can work with types:
- Boolean
- DateTime
- String
- List of string
- List of double
- Null
- Object
Object is a special type to allow using, for example, services inside function.
public void Should_Evaluate_Service()
{
// arrange
var service = _serviceFactory(null);
var args = new Dictionary<string, EvaluatorValue>
{
["myService"] = new EvaluatorValue(new MyService()),
["myNum"] = 10
};
var expected = 50.5;
// act
var actual = service.CalcNumber("TestService(myService; myNum)", args);
// assert
actual.Should().BeApproximatelyNumber(expected);
}
private class ServiceFunction : IFunction
{
public string Name { get; } = "TestService";
public bool CanHandleNullArguments { get; }
public InternalEvaluatorValue Execute(List<InternalEvaluatorValue> args, ValueFactory factory)
{
return args[0].GetObject<MyService>().GetTemperature() + args[1];
}
}
private class MyService
{
public double GetTemperature()
{
return 40.5;
}
}
List description
You can describe a list inside the formula
| Example | Result | | ------------ | ------------ | | IsMember({'printer', 'computer', 'monitor'}; 'computer') | 1 | | Unique({'NEW','OLD','NEW','HEAVEN','OLD'}) | {'NEW','OLD','HEAVEN'} | | Add({1, 2, 3, 10, 3}) | 19 |
Variables
You can use two types of variables:
- Simple variable
- Bordered variable
Simple variable
Simple variable means that it named without unique symbols and starts with a letter. Only one extra symbol is possible, it's "_"
Some examples:
- "25 + myArgument - 1"
- "25 + myArg1 - 2"
- "arg5684argArg_arg"
- "25 + myArgument_newAge - 3"
Bordered variable
Bordered variable means that it has a tricky name with any symbols, except for square brackets.
Some examples:
- "25 + [myVariable and some words] - 1"
- "25 + [
