Skip to content

Commit

Permalink
#720 - New Results API.
Browse files Browse the repository at this point in the history
  • Loading branch information
sys27 committed Sep 12, 2023
1 parent fd94f7d commit 92ad27f
Show file tree
Hide file tree
Showing 74 changed files with 1,881 additions and 1,043 deletions.
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,17 @@ _Note: The `Parse` method won't simplify the expression automatically, it will r

**Solve:**

This method parses string expression (like the `Parse` method) and then calculates it (returns object which implements the `IResult` interface).

There are two overloads of this method (common and generic). The "common" returns just `IResult` (you can access result by `Result` property). The generic allows to return specific implementation of `IResult` (eg. `NumberResult`).
This method parses string expression (like the `Parse` method) and then calculates it (returns object which implements the `Result` abstract class).

```csharp
var processor = new Processor();
processor.Solve<NumberResult>("2 + 2"); // will return 4.0 (double)
// or
var result = processor.Solve("2 + 2");

processor.Solve("2 + 2").Result; // will return 4.0 (object)
Console.WriteLine(result); // 4.0
```

The `result` variable will contain `4` (as `NumberResult` which is implementation of the `Result` class). Basically, it is a hand-made implementation of discriminated union. The `Result` class provides the abstraction (root class) for DU, whereas implementation for each possible return type is dedicated for appropriate nested result class. Check documentation for more examples: [Result](https://sys27.github.io/xFunc/api/xFunc.Maths.Results.Result.html), [Processor](https://sys27.github.io/xFunc/api/xFunc.Maths.Processor.html).

If your expression has any parameter, you need to assign a value to it (otherwise xFunc will throw an exception), because `Processor` has a build-in collection of parameters and user functions, you don't need to use `ExpressionParameters` directly:

```csharp
Expand All @@ -76,14 +74,12 @@ processor.Solve("x := 10");
processor.Parameters.Variables.Add("x", 10);
```

_Note: The `Solve` method automatically simplifies expression, to control this behavior you can use the `simplify` argument. It's useful for differentiation because it will eliminate unnecessary expression nodes._

**Simplify:**

```csharp
var processor = new Processor();

processor.Solve<LambdaResult>("simplify((x) => arcsin(sin(x)))");
processor.Solve("simplify((x) => arcsin(sin(x)))");
// or
processor.Simplify("arcsin(sin(x))");
// will return simplified expression = "x"
Expand All @@ -96,7 +92,7 @@ _Detailed [simplification rules](https://sys27.github.io/xFunc/articles/simplifi
```csharp
var processor = new Processor();

processor.Solve<LambdaResult>("deriv((x) => 2x)");
processor.Solve("deriv((x) => 2x)");
// or
processor.Differentiate("2x");
// will return "2"
Expand Down
4 changes: 2 additions & 2 deletions xFunc.Benchmark/Benchmarks/ProcessorBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ public IExpression Parse()
=> processor.Parse("(100.1 + 2 * (3 * sin(4 * cos(5 * tan(6 * ctg(10 * x)))) * 3) / (func(a, b, c) ^ 2)) - (cos(y) - 111.3) & (true | false impl true eq false) + (det({{1, 2}, {3, 4}}) * 10 * log(2, 3)) + re(3 + 2 * i) - im(2 - 9 * i) + (9 + 2 * i)");

[Benchmark]
public NumberResult Solve()
=> processor.Solve<NumberResult>("count(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + (2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5 + 2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5 + 2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5 + 2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5) * 10 ^ 6 + 10!");
public Result Solve()
=> processor.Solve("count(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + (2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5 + 2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5 + 2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5 + 2 * sin(4 * cos(6 * tan(8 * cot(pi / 4) ^ 2) ^ 3) ^ 4) ^ 5) * 10 ^ 6 + 10!");
}
19 changes: 19 additions & 0 deletions xFunc.Maths/Expressions/EmptyValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Dmytro Kyshchenko. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace xFunc.Maths.Expressions;

/// <summary>
/// Represents the empty value (needed for functions that don't return any value).
/// </summary>
public sealed class EmptyValue
{
private EmptyValue()
{
}

/// <summary>
/// Gets the single instance of <see cref="EmptyValue"/>.
/// </summary>
public static EmptyValue Instance { get; } = new EmptyValue();
}
2 changes: 1 addition & 1 deletion xFunc.Maths/Expressions/Programming/For.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public override object Execute(ExpressionParameters? parameters)
Iteration.Execute(nested);
}

return null!; // TODO:
return EmptyValue.Instance;
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion xFunc.Maths/Expressions/Programming/While.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public object Execute(ExpressionParameters? parameters)
Body.Execute(nested);
}

return null!; // TODO:
return EmptyValue.Instance;
}

/// <inheritdoc />
Expand Down
159 changes: 49 additions & 110 deletions xFunc.Maths/Processor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ namespace xFunc.Maths;
/// <para>To evaluate the function:</para>
/// <code>
/// var processor = new Processor();
/// var result = processor.Solve&lt;NumberResult&gt;("2 + 2");
/// var result = processor.Solve("2 + 2");
/// </code>
///
/// <para>To simplify the expression:</para>
/// <code>
/// var processor = new Processor();
/// var result = processor.Solve&lt;LambdaResult&gt;("simplify((x) => arcsin(sin(x)))");
/// var result = processor.Solve("simplify((x) => arcsin(sin(x)))");
/// </code>
///
/// <para>To differentiate the expression:</para>
/// <code>
/// var processor = new Processor();
/// var result = processor.Solve&lt;LambdaResult&gt;("deriv((x) => 2x)");
/// var result = processor.Solve("deriv((x) => 2x)");
/// </code>
/// </example>
public class Processor
Expand Down Expand Up @@ -111,158 +111,97 @@ public Processor()
/// <summary>
/// Evaluates the specified expression.
/// </summary>
/// <param name="function">The function.</param>
/// <param name="expression">The expression.</param>
/// <remarks>
/// <para>This method returns untyped <see cref="IResult"/>. Usually this method is useful when you just need a result of evaluation of <paramref name="function"/>, otherwise check generic version of this method.</para>
/// <para>If your expression contains any parameter or user-defined function, then they will be resolved from the built-in <see cref="Parameters"/> collection.</para>
/// <para>If your expression contains any parameter or user-defined function, then they will be resolved from the built-in <see cref="Parameters"/> collection.</para>
/// </remarks>
/// <returns>The result of evaluation.</returns>
/// <seealso cref="IResult"/>
public IResult Solve(string function)
/// <example>
/// <code>
/// var processor = new Processor();
/// var result = processor.Solve("2 + 2");
/// </code>
/// </example>
/// <seealso cref="Result"/>
/// <seealso cref="Result.AngleResult"/>
/// <seealso cref="Result.AreaResult"/>
/// <seealso cref="Result.BooleanResult"/>
/// <seealso cref="Result.ComplexNumberResult"/>
/// <seealso cref="Result.EmptyResult"/>
/// <seealso cref="Result.LambdaResult"/>
/// <seealso cref="Result.LengthResult"/>
/// <seealso cref="Result.MassResult"/>
/// <seealso cref="Result.MatrixResult"/>
/// <seealso cref="Result.NumberResult"/>
/// <seealso cref="Result.PowerResult"/>
/// <seealso cref="Result.StringResult"/>
/// <seealso cref="Result.TemperatureResult"/>
/// <seealso cref="Result.TimeResult"/>
/// <seealso cref="Result.VectorResult"/>
/// <seealso cref="Result.VolumeResult"/>
public Result Solve(string expression)
{
var exp = Parse(function);
var exp = Parse(expression);
exp.Analyze(typeAnalyzer);

var result = exp.Execute(Parameters);
return result switch
{
NumberValue number
=> new NumberResult(number.Number),
=> new Result.NumberResult(number),

AngleValue angle
=> new AngleNumberResult(angle),
=> new Result.AngleResult(angle),

PowerValue power
=> new PowerNumberResult(power),
=> new Result.PowerResult(power),

TemperatureValue temperature
=> new TemperatureNumberResult(temperature),
=> new Result.TemperatureResult(temperature),

MassValue mass
=> new MassNumberResult(mass),
=> new Result.MassResult(mass),

LengthValue length
=> new LengthNumberResult(length),
=> new Result.LengthResult(length),

TimeValue time
=> new TimeNumberResult(time),
=> new Result.TimeResult(time),

AreaValue area
=> new AreaNumberResult(area),
=> new Result.AreaResult(area),

VolumeValue volume
=> new VolumeNumberResult(volume),
=> new Result.VolumeResult(volume),

Complex complex
=> new ComplexNumberResult(complex),
=> new Result.ComplexNumberResult(complex),

bool boolean
=> new BooleanResult(boolean),
=> new Result.BooleanResult(boolean),

string str
=> new StringResult(str),
=> new Result.StringResult(str),

Lambda lambda
=> new LambdaResult(lambda),
=> new Result.LambdaResult(lambda),

VectorValue vectorValue
=> new VectorValueResult(vectorValue),
=> new Result.VectorResult(vectorValue),

MatrixValue matrixValue
=> new MatrixValueResult(matrixValue),
=> new Result.MatrixResult(matrixValue),

RationalValue rationalValue
=> new RationalValueResult(rationalValue),
=> new Result.RationalResult(rationalValue),

EmptyValue
=> new Result.EmptyResult(),

Check warning on line 199 in xFunc.Maths/Processor.cs

View check run for this annotation

Codecov / codecov/patch

xFunc.Maths/Processor.cs#L199

Added line #L199 was not covered by tests

_ => throw new InvalidResultException(),
};
}

/// <summary>
/// Evaluates the specified function.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="function">The function.</param>
/// <remarks>
/// <para>If your expression contains any parameter or user-defined function, then they will be resolved from the built-in <see cref="Parameters"/> collection.</para>
/// <para>
/// This method returns specific implementation of <see cref="IResult"/> and by accessing <see cref="IResult.Result"/> property you will be able to get typed result, for example, <c>double</c> instead of <c>object</c>.
/// </para>
/// <para><see cref="IResult"/> implementations:</para>
/// <list type="bullet">
/// <item>
/// <term><see cref="AngleNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="AreaNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="BooleanResult"/></term>
/// </item>
/// <item>
/// <term><see cref="ComplexNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="LambdaResult"/></term>
/// </item>
/// <item>
/// <term><see cref="LengthNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="MassNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="MatrixValueResult"/></term>
/// </item>
/// <item>
/// <term><see cref="NumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="PowerNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="StringResult"/></term>
/// </item>
/// <item>
/// <term><see cref="TemperatureNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="TimeNumberResult"/></term>
/// </item>
/// <item>
/// <term><see cref="VectorValueResult"/></term>
/// </item>
/// <item>
/// <term><see cref="VolumeNumberResult"/></term>
/// </item>
/// </list>
/// </remarks>
/// <returns>The result of evaluation.</returns>
/// <example>
/// <code>
/// var processor = new Processor();
/// var result = processor.Solve&lt;NumberResult&gt;("2 + 2");
/// </code>
/// </example>
/// <seealso cref="IResult"/>
/// <seealso cref="AngleNumberResult"/>
/// <seealso cref="AreaNumberResult"/>
/// <seealso cref="BooleanResult"/>
/// <seealso cref="ComplexNumberResult"/>
/// <seealso cref="LambdaResult"/>
/// <seealso cref="LengthNumberResult"/>
/// <seealso cref="MassNumberResult"/>
/// <seealso cref="MatrixValueResult"/>
/// <seealso cref="NumberResult"/>
/// <seealso cref="PowerNumberResult"/>
/// <seealso cref="StringResult"/>
/// <seealso cref="TemperatureNumberResult"/>
/// <seealso cref="TimeNumberResult"/>
/// <seealso cref="VectorValueResult"/>
/// <seealso cref="VolumeNumberResult"/>
public TResult Solve<TResult>(string function) where TResult : IResult
=> (TResult)Solve(function);

/// <summary>
/// Simplifies the <paramref name="function"/>.
/// </summary>
Expand Down
25 changes: 0 additions & 25 deletions xFunc.Maths/Results/AngleNumberResult.cs

This file was deleted.

25 changes: 0 additions & 25 deletions xFunc.Maths/Results/AreaNumberResult.cs

This file was deleted.

0 comments on commit 92ad27f

Please sign in to comment.