diff --git a/.gitignore b/.gitignore
index b011f884..dedb186c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,9 +7,6 @@
/src/Mathematics.NET.DevApp/*.cs
*.runsettings
- # Temporary
-/src/Mathematics.NET.SourceGenerators/
-
# User-specific files
*.rsuser
*.suo
diff --git a/README.md b/README.md
index b59279e3..f43e657e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
Mathematics.NET
+# Mathematics.NET
-Mathematics.NET is a C# class library that provides tools for solving mathematical problems.
+Mathematics.NET is a C# class library that provides tools for solving mathematical problems.
[![GitHub](https://img.shields.io/github/license/HamletTanyavong/Mathematics.NET?style=flat-square&logo=github&labelColor=87cefa&color=ffd700)](https://github.com/HamletTanyavong/Mathematics.NET)
[![GitHub Repo Stars](https://img.shields.io/github/stars/HamletTanyavong/Mathematics.NET?color=87cefa&style=flat-square&logo=github)](https://github.com/HamletTanyavong/Mathematics.NET/stargazers)
diff --git a/docs/articles/fundamentals/numeric-types.md b/docs/articles/fundamentals/numeric-types.md
index e36e8ac6..a6d0293b 100644
--- a/docs/articles/fundamentals/numeric-types.md
+++ b/docs/articles/fundamentals/numeric-types.md
@@ -2,21 +2,21 @@
There are three numeric types that can be used to represent complex, real, and rational numbers in Mathematics.NET.
-All Mathematics.NET numbers implement the [IComplex](https://mathematics.hamlettanyavong.com/api/Mathematics.NET.Core.IComplex-2.html) interface. Particularly useful is the fact that, unlike .NET runtime's [INumberBase\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs), `IComplex` defines the `Conjugate` method; this is incredibly helpful in avoiding code duplication for calculations involving complex and real numbers.
+All Mathematics.NET numbers implement the [IComplex\](https://mathematics.hamlettanyavong.com/api/Mathematics.NET.Core.IComplex-1.html) interface. Particularly useful is the fact that, unlike .NET runtime's [INumberBase\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs), `IComplex` defines the `Conjugate` method; this is helpful in avoiding code duplication for calculations involving complex and real numbers.
## Floating-Point Types
-Floating-point Mathematics.NET numbers may be backed any number that implements [IFloatingPointIee754\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/IFloatingPointIeee754.cs) and [IMinMaxValue\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/IMinMaxValue.cs), or more specifically, [float](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Single.cs), [double](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Double.cs), and [decimal](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Decimal.cs).
+Floating-point Mathematics.NET numbers are backed by the [double](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Double.cs) numeric type.
### Complex Numbers
-To create a complex number, we choose a backing type, in this case `double`, and write
+To create a complex number, we can write
```csharp
-Complex z = new(3, 4);
+ComplexNumber z = new(3, 4);
```
This represents the number $ z = 3+i4 $. We can also specify only one number to create a complex number with no imaginary part
```csharp
-Complex z = 3;
+ComplexNumber z = 3;
```
which represents $ z = 3 $.
@@ -24,15 +24,12 @@ which represents $ z = 3 $.
Likewise, to create a real number, write
```csharp
-Real z = 1;
+Real x = 1.23;
```
-With real numbers, we can also get maximum and minimum values which will depend on the backing type.
+To get is backing value, write
```csharp
-Console.WriteLine("Max value with float backing type: {0}", Real.MaxValue);
-Console.WriteLine("Max value with double backing type: {0}", Real.MaxValue);
+double backingValue = x.Value;
```
-This will output `Max value with float backing type: 3.4028235E+38`
-and `Max value with double backing type: 1.7976931348623157E+308`.
## Binary Types
@@ -40,29 +37,22 @@ Rational numbers are the only Mathematics.NET type in this category.
### Rational Numbers
-Rational numbers require two backing types, one that implements [IBinaryInteger\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs) and one that implements both [IFloatingPointIee754\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/IFloatingPointIeee754.cs) and [IMinMaxValue\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/IMinMaxValue.cs).
+Rational numbers require a type parameter that implements [IBinaryInteger\](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs). The type specified here is used to represent the numerator and denominator of the rational number.
With this information, we can create the following rational numbers:
```csharp
-Rational a = 2;
-Rational b = new(2, 3);
-Rational c = new(3, 4);
+Rational a = 2;
+Rational b = new(2, 3);
+Rational c = new(3, 4);
```
which represent $ a = 2 $, $ b = 2/3 $, and $ c = 3/4 $.
-The first type parameter indicates that the constructor only accepts values of that type. In these cases, `a` must be an int, `b` must be a byte, and `c` must be a BigInteger. The second parameter indicates the desired floating-point type with which we want to represent the rational number. We can get this value in two ways, e.g.
-```csharp
-Console.WriteLine(b.Value);
-Console.WriteLine((float)b);
-```
-which will both output `0.6666667`.
-
> [!CAUTION]
> The floating-point representation of rational numbers may not be accurate in all cases.
-We can also convert a floating-point number into a rational number with an explicit cast
+We can also convert a double into a rational number with an explicit cast
```csharp
-Console.WriteLine((Rational)3.14);
+Console.WriteLine((Rational)3.14);
```
> [!NOTE]
> The conversion conversion is not guaranteed to create the "best" fraction; for instance, the value $ 0.3333333333333333 $ will not produce $ 1/3 $ but instead produce $ 8333333333333331 / 25000000000000000 $.
diff --git a/docs/articles/get_started/installation.md b/docs/articles/get_started/installation.md
index 8487e6ec..39de7618 100644
--- a/docs/articles/get_started/installation.md
+++ b/docs/articles/get_started/installation.md
@@ -4,7 +4,7 @@ Mathematics.NET is available to download from [nuget](https://www.nuget.org/pack
# [Package Reference](#tab/package-reference)
-To use Mathematics.NET in you project, add the following line to your .csproj file:
+To use Mathematics.NET in your project, add the following line to your .csproj file:
```xml
```
diff --git a/src/Mathematics.NET.DevApp/Mathematics.NET.DevApp.csproj b/src/Mathematics.NET.DevApp/Mathematics.NET.DevApp.csproj
index 10014264..db52578b 100644
--- a/src/Mathematics.NET.DevApp/Mathematics.NET.DevApp.csproj
+++ b/src/Mathematics.NET.DevApp/Mathematics.NET.DevApp.csproj
@@ -10,6 +10,7 @@
+
diff --git a/src/Mathematics.NET.SourceGenerators/Extensions.cs b/src/Mathematics.NET.SourceGenerators/Extensions.cs
new file mode 100644
index 00000000..cbd8e6a4
--- /dev/null
+++ b/src/Mathematics.NET.SourceGenerators/Extensions.cs
@@ -0,0 +1,59 @@
+//
+// Mathematics.NET
+// https://github.com/HamletTanyavong/Mathematics.NET
+//
+// MIT License
+//
+// Copyright (c) 2023 Hamlet Tanyavong
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+namespace Mathematics.NET.SourceGenerators;
+
+public static class Extensions
+{
+ public static string? GetValue(this NameSyntax name)
+ {
+ return name switch
+ {
+ SimpleNameSyntax simpleNameSyntax => simpleNameSyntax.Identifier.Text,
+ QualifiedNameSyntax qualifiedNameSyntax => qualifiedNameSyntax.Right.Identifier.Text,
+ _ => null
+ };
+ }
+
+ // TODO: Determine if every equation should/will have an attribute to remove.
+ public static MethodDeclarationSyntax RemoveEquationAttribute(this MethodDeclarationSyntax methodDeclarationSyntax)
+ {
+ var equationAttributeNode = methodDeclarationSyntax
+ .DescendantNodes()
+ .OfType()
+ .First(syntax => syntax.Name.GetValue() is "Equation" or "EquationAttribute");
+
+ if (equationAttributeNode.Parent!.ChildNodes().Count() > 1)
+ {
+ return methodDeclarationSyntax.RemoveNode(equationAttributeNode, SyntaxRemoveOptions.KeepNoTrivia)!;
+ }
+ else
+ {
+ return methodDeclarationSyntax.RemoveNode(equationAttributeNode.Parent, SyntaxRemoveOptions.KeepNoTrivia)!;
+ }
+ }
+}
diff --git a/src/Mathematics.NET.SourceGenerators/IncrementalGenerators/DerivativeGenerator.cs b/src/Mathematics.NET.SourceGenerators/IncrementalGenerators/DerivativeGenerator.cs
new file mode 100644
index 00000000..034dfb0d
--- /dev/null
+++ b/src/Mathematics.NET.SourceGenerators/IncrementalGenerators/DerivativeGenerator.cs
@@ -0,0 +1,63 @@
+//
+// Mathematics.NET
+// https://github.com/HamletTanyavong/Mathematics.NET
+//
+// MIT License
+//
+// Copyright (c) 2023 Hamlet Tanyavong
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+using System.Collections.Immutable;
+using System.Text;
+using Mathematics.NET.SourceGenerators.Models;
+using Mathematics.NET.SourceGenerators.SourceBuilders;
+
+namespace Mathematics.NET.SourceGenerators.IncrementalGenerators;
+
+/// A generator for calculating derivatives
+[Generator]
+public sealed class DerivativeGenerator : IIncrementalGenerator
+{
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ var provider = context.SyntaxProvider
+ .CreateSyntaxProvider(CouldBeEquationAttribute, GetEquationOrNull)
+ .Where(info => info is not null);
+ var compilation = context.CompilationProvider.Combine(provider.Collect());
+ context.RegisterSourceOutput(compilation, (context, source) => GenerateCode(context, source.Left, source.Right!));
+ }
+
+ private static bool CouldBeEquationAttribute(SyntaxNode syntaxNode, CancellationToken cancellationToken)
+ => syntaxNode is AttributeSyntax attributeSyntax && attributeSyntax.Name.GetValue() is "Equation" or "EquationAttribute";
+
+ private static MethodInformation? GetEquationOrNull(GeneratorSyntaxContext context, CancellationToken cancellationToken)
+ {
+ // The method syntax will not be null if attribute syntax is not null since the attribute can only be applied to methods.
+ var attribute = (AttributeSyntax)context.Node;
+ return new(attribute, (MethodDeclarationSyntax)attribute.Parent!.Parent!);
+ }
+
+ private void GenerateCode(SourceProductionContext context, Compilation compilation, ImmutableArray methodInformation)
+ {
+ var derivatives = new DerivativesBuilder(compilation, methodInformation);
+ context.AddSource("Equations.g.cs", derivatives.GenerateSource().GetText(Encoding.UTF8).ToString());
+ }
+}
diff --git a/src/Mathematics.NET.SourceGenerators/Mathematics.NET.SourceGenerators.csproj b/src/Mathematics.NET.SourceGenerators/Mathematics.NET.SourceGenerators.csproj
index 04fc35d2..ea490301 100644
--- a/src/Mathematics.NET.SourceGenerators/Mathematics.NET.SourceGenerators.csproj
+++ b/src/Mathematics.NET.SourceGenerators/Mathematics.NET.SourceGenerators.csproj
@@ -4,6 +4,7 @@
true
false
latest
+ enable
netstandard2.0
x64
enable
diff --git a/src/Mathematics.NET.SourceGenerators/Models/MethodInformation.cs b/src/Mathematics.NET.SourceGenerators/Models/MethodInformation.cs
new file mode 100644
index 00000000..7bbe38ae
--- /dev/null
+++ b/src/Mathematics.NET.SourceGenerators/Models/MethodInformation.cs
@@ -0,0 +1,42 @@
+//
+// Mathematics.NET
+// https://github.com/HamletTanyavong/Mathematics.NET
+//
+// MIT License
+//
+// Copyright (c) 2023 Hamlet Tanyavong
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+namespace Mathematics.NET.SourceGenerators.Models;
+
+/// A class containing information about a specific method
+public sealed class MethodInformation
+{
+ public MethodInformation(AttributeSyntax attributeSyntax, MethodDeclarationSyntax methodDeclaration)
+ {
+ AttributeSyntax = attributeSyntax;
+ MethodDeclaration = methodDeclaration;
+ }
+
+ public AttributeSyntax AttributeSyntax { get; }
+
+ public MethodDeclarationSyntax MethodDeclaration { get; }
+}
diff --git a/src/Mathematics.NET.SourceGenerators/SourceBuilders/DerivativesBuilder.cs b/src/Mathematics.NET.SourceGenerators/SourceBuilders/DerivativesBuilder.cs
new file mode 100644
index 00000000..4ee72477
--- /dev/null
+++ b/src/Mathematics.NET.SourceGenerators/SourceBuilders/DerivativesBuilder.cs
@@ -0,0 +1,101 @@
+//
+// Mathematics.NET
+// https://github.com/HamletTanyavong/Mathematics.NET
+//
+// MIT License
+//
+// Copyright (c) 2023 Hamlet Tanyavong
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+using System.Collections.Immutable;
+using Mathematics.NET.SourceGenerators.Models;
+using Microsoft.CodeAnalysis.CSharp;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Mathematics.NET.SourceGenerators.SourceBuilders;
+
+/// Derivatives builder
+public sealed class DerivativesBuilder
+{
+ private readonly string _assemblyName;
+ private readonly ImmutableArray _methodInformation;
+
+ public DerivativesBuilder(Compilation compilationUnitSyntax, ImmutableArray methodInformation)
+ {
+ _assemblyName = compilationUnitSyntax.AssemblyName!;
+ _methodInformation = methodInformation;
+ }
+
+ public CompilationUnitSyntax GenerateSource()
+ {
+ var members = GenerateMembers();
+ return CreateCompilationUnit(members);
+ }
+
+ private CompilationUnitSyntax CreateCompilationUnit(MemberDeclarationSyntax[] memberDeclarations)
+ {
+ return CompilationUnit()
+ .WithUsings(
+ SingletonList(
+ UsingDirective(
+ QualifiedName(
+ QualifiedName(
+ IdentifierName("Mathematics"),
+ IdentifierName("NET")),
+ IdentifierName("Core")))
+ .WithUsingKeyword(
+ Token(
+ TriviaList(
+ Comment("// Auto-generated code")),
+ SyntaxKind.UsingKeyword,
+ TriviaList()))))
+ .WithMembers(
+ SingletonList(
+ FileScopedNamespaceDeclaration(
+ QualifiedName(
+ QualifiedName(
+ IdentifierName(_assemblyName),
+ IdentifierName("Generated")),
+ IdentifierName("Mathematics")))
+ .WithMembers(
+ SingletonList(
+ ClassDeclaration("Equations")
+ .WithModifiers(
+ TokenList(new[] {
+ Token(SyntaxKind.PublicKeyword),
+ Token(SyntaxKind.StaticKeyword) }))
+ .WithMembers(
+ List(memberDeclarations))))))
+ .NormalizeWhitespace();
+ }
+
+ private MemberDeclarationSyntax[] GenerateMembers()
+ {
+ var result = new MemberDeclarationSyntax[_methodInformation.Length];
+ for (int i = 0; i < _methodInformation.Length; i++)
+ {
+ var equation = _methodInformation[i].MethodDeclaration.RemoveEquationAttribute();
+ var transformedEquation = SymbolicsHelper.TransformEquation(equation);
+ result[i] = transformedEquation;
+ }
+ return result;
+ }
+}
diff --git a/src/Mathematics.NET.SourceGenerators/SymbolicsHelper.cs b/src/Mathematics.NET.SourceGenerators/SymbolicsHelper.cs
new file mode 100644
index 00000000..425f463b
--- /dev/null
+++ b/src/Mathematics.NET.SourceGenerators/SymbolicsHelper.cs
@@ -0,0 +1,123 @@
+//
+// Mathematics.NET
+// https://github.com/HamletTanyavong/Mathematics.NET
+//
+// MIT License
+//
+// Copyright (c) 2023 Hamlet Tanyavong
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+using Microsoft.CodeAnalysis.CSharp;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+
+namespace Mathematics.NET.SourceGenerators;
+
+public static class SymbolicsHelper
+{
+ // TODO: Reduce mathematical expressions after taking derivatives
+ public static MethodDeclarationSyntax TransformEquation(MethodDeclarationSyntax methodDeclarationSyntax)
+ {
+ MethodDeclarationSyntax transformation = methodDeclarationSyntax;
+ while (CanTakeInnerDerivatives(transformation, out var derivativeExpressions))
+ {
+ foreach (var derivativeExpression in derivativeExpressions)
+ {
+ var flattenedDerivativeExpression = ComputeDerivative(derivativeExpression);
+ transformation = transformation.ReplaceNode(derivativeExpression.Parent!, flattenedDerivativeExpression ?? derivativeExpression);
+ }
+ }
+ return transformation;
+ }
+
+ // We want to resolve the innermost derivative first, so we choose the derivative
+ // expression with no other derivative expressions present in its descendant nodes.
+ private static bool CanTakeInnerDerivatives(MethodDeclarationSyntax syntaxNode, out IEnumerable derivatives)
+ {
+ var descendantNodes = syntaxNode.DescendantNodes();
+ derivatives = descendantNodes
+ .OfType()
+ .Where(x => x.Name.Identifier.ValueText == "Dif" && !x
+ .DescendantNodes()
+ .OfType()
+ .Any(x => x.Name.Identifier.ValueText == "Dif"));
+ return derivatives.Count() > 0;
+ }
+
+ // TODO: Account for higher order derivatives
+ // TODO: Take into account differentiating with respect to multiple variables
+ private static ExpressionSyntax? ComputeDerivative(MemberAccessExpressionSyntax memberAccessExpressionSyntax)
+ {
+ // memberAccessExpressionSyntax is the Dif expression extracted
+ var invocationExpression = (InvocationExpressionSyntax)memberAccessExpressionSyntax.Parent!;
+
+ var derivativeExpression = (InvocationExpressionSyntax)GetDifferentiableExpression(invocationExpression.ArgumentList.Arguments[0])!;
+ var method = (MemberAccessExpressionSyntax)derivativeExpression.Expression;
+ var args = derivativeExpression.ArgumentList;
+ return method.Name.Identifier.ValueText switch
+ {
+ "Cos" => DifCos(method, args),
+ "Sin" => DifSin(method, args),
+ _ => null
+ };
+ }
+
+ private static ExpressionSyntax? GetDifferentiableExpression(ArgumentSyntax argumentSyntax)
+ {
+ return argumentSyntax.Expression switch
+ {
+ SimpleLambdaExpressionSyntax simpleLambdaExpressionSyntax => simpleLambdaExpressionSyntax.ExpressionBody,
+ _ => null
+ };
+ }
+
+ // Mathematical functions
+
+ private static ExpressionSyntax DifCos(MemberAccessExpressionSyntax memberAccessExpressionSyntax, ArgumentListSyntax argumentListSyntax)
+ {
+ var cosNameSyntax = memberAccessExpressionSyntax
+ .DescendantNodes()
+ .OfType()
+ .First(x => x.Identifier.ValueText == "Cos");
+ var newExpression = memberAccessExpressionSyntax.ReplaceNode(cosNameSyntax, IdentifierName("Sin"));
+ return BinaryExpression(
+ SyntaxKind.MultiplyExpression,
+ PrefixUnaryExpression(
+ SyntaxKind.UnaryMinusExpression,
+ LiteralExpression(
+ SyntaxKind.NumericLiteralExpression,
+ Literal(1))),
+ InvocationExpression(
+ newExpression,
+ argumentListSyntax));
+ }
+
+ private static ExpressionSyntax DifSin(MemberAccessExpressionSyntax memberAccessExpressionSyntax, ArgumentListSyntax argumentListSyntax)
+ {
+ var sinNameSyntax = memberAccessExpressionSyntax
+ .DescendantNodes()
+ .OfType()
+ .First(x => x.Identifier.ValueText == "Sin");
+ var newExpression = memberAccessExpressionSyntax.ReplaceNode(sinNameSyntax, IdentifierName("Cos"));
+ return InvocationExpression(
+ newExpression,
+ argumentListSyntax);
+ }
+}
diff --git a/src/Mathematics.NET/Core/Complex.cs b/src/Mathematics.NET/Core/Complex.cs
deleted file mode 100644
index f7474e9d..00000000
--- a/src/Mathematics.NET/Core/Complex.cs
+++ /dev/null
@@ -1,674 +0,0 @@
-//
-// Mathematics.NET
-// https://github.com/HamletTanyavong/Mathematics.NET
-//
-// MIT License
-//
-// Copyright (c) 2023 Hamlet Tanyavong
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace Mathematics.NET.Core;
-
-/// Represents a complex number
-/// A type that implements and
-[Serializable]
-[StructLayout(LayoutKind.Sequential)]
-public readonly struct Complex
- : IComplex, T>,
- IDifferentiableFunctions, T>
- where T : IFloatingPointIeee754, IMinMaxValue
-{
- private static readonly Complex s_im = new(Real.Zero, Real.One);
- private static readonly Complex s_imOverTwo = new(Real.Zero, Real.One / Real.Two);
-
- private static readonly Complex s_oneOverTwo = T.CreateTruncating(0.5);
- private static readonly Complex s_three = T.CreateTruncating(3.0);
- private static readonly Real s_four = T.CreateTruncating(4.0);
-
- // For computing Asin and Acos
- private static readonly Real s_asinOverflowThreshold = Real.Sqrt(Real.MaxValue) / Real.Two;
- private static readonly Real s_threeOverFour = T.CreateTruncating(0.75);
- private static readonly Real s_threeOverTwo = T.CreateTruncating(1.5);
-
- public static readonly Complex Zero = Real.Zero;
- public static readonly Complex One = Real.One;
- public static readonly Complex Two = Real.Two;
-
- public static readonly Complex NaN = new(Real.NaN, Real.NaN);
- public static readonly Complex Infinity = new(Real.PositiveInfinity, Real.PositiveInfinity);
-
- private readonly Real _real;
- private readonly Real _imaginary;
-
- public Complex(Real real)
- {
- _real = real;
- _imaginary = T.Zero;
- }
-
- public Complex(Real real, Real imaginary)
- {
- _real = real;
- _imaginary = imaginary;
- }
-
- //
- // Complex number properties
- //
-
- public Real Re => _real;
- public Real Im => _imaginary;
-
- public Real Magnitude => Hypot(_real.Value, _imaginary.Value);
- public Real Phase => T.Atan2(_imaginary.Value, _real.Value);
-
- //
- // Constants
- //
-
- static Complex IComplex, T>.Zero => Zero;
- static Complex IComplex, T>.One => One;
- static Complex IComplex, T>.Two => Two;
- static Complex IComplex, T>.NaN => NaN;
-
- //
- // Operators
- //
-
- public static Complex operator -(Complex z) => new(-z._real, -z._imaginary);
-
- public static Complex operator +(Complex z, Complex w) => new(z._real + w._real, z._imaginary + w._imaginary);
-
- public static Complex operator -(Complex z, Complex w) => new(z._real - w._real, z._imaginary - w._imaginary);
-
- public static Complex operator *(Complex z, Complex w)
- => new(z._real * w._real - z._imaginary * w._imaginary, z._real * w._imaginary + w._real * z._imaginary);
-
- // From Michael Baudin and Robert L. Smith
- public static Complex operator /(Complex z, Complex w)
- {
- var zRe = z._real.Value;
- var zIm = z._imaginary.Value;
- var wRe = w._real.Value;
- var wIm = w._imaginary.Value;
-
- T reResult;
- T imResult;
- if (T.Abs(wIm) <= T.Abs(wRe))
- {
- DivisionHelper(zRe, zIm, wRe, wIm, out reResult, out imResult);
- }
- else
- {
- DivisionHelper(zIm, zRe, wIm, wRe, out reResult, out imResult);
- imResult = -imResult;
- }
-
- return new(reResult, imResult);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void DivisionHelper(T x, T y, T maxW, T minW, out T real, out T imaginary)
- {
- var ratio = minW / maxW;
- var scale = T.One / (maxW + minW * ratio);
- if (ratio != T.Zero)
- {
- real = (x + y * ratio) * scale;
- imaginary = (y - x * ratio) * scale;
- }
- else
- {
- real = (x + minW * (y / maxW)) * scale;
- imaginary = (y - minW * (x / maxW)) * scale;
- }
- }
-
- //
- // Equality
- //
-
- public static bool operator ==(Complex left, Complex right) => left.Re == right.Re && left.Im == right.Im;
-
- public static bool operator !=(Complex left, Complex right) => left.Re != right.Re || left.Im != right.Im;
-
- public override bool Equals([NotNullWhen(true)] object? obj) => obj is Complex other && Equals(other);
-
- public bool Equals(Complex value) => _real.Equals(value.Re) && _imaginary.Equals(value.Im);
-
- public override int GetHashCode() => HashCode.Combine(_real, _imaginary);
-
- //
- // Formatting
- //
-
- public override string ToString() => ToString(null, null);
-
- public string ToString(string? format, IFormatProvider? provider)
- {
- format = string.IsNullOrEmpty(format) ? "ALL" : format.ToUpperInvariant();
- provider ??= NumberFormatInfo.InvariantInfo;
-
- if (format is "ALL")
- {
- return string.Format(provider, "({0}, {1})", _real.ToString(null, provider), _imaginary.ToString(null, provider));
- }
- else if (format is "RE")
- {
- return string.Format(provider, "{0}", _real.ToString(null, provider));
- }
- else if (format is "IM")
- {
- return string.Format(provider, "{0}", _imaginary.ToString(null, provider));
- }
- else
- {
- throw new FormatException($"The \"{format}\" format is not supported.");
- }
- }
-
- public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider)
- {
- format = format.IsEmpty ? "ALL" : format.ToString().ToUpperInvariant();
- provider ??= NumberFormatInfo.InvariantInfo;
-
- if (format is "ALL")
- {
- // There are a minimum of 6 characters for "(0, 0)".
- int charsCurrentlyWritten = 0;
- if (destination.Length < 6)
- {
- charsWritten = charsCurrentlyWritten;
- return false;
- }
-
- destination[charsCurrentlyWritten++] = '(';
-
- bool tryFormatSucceeded = _real.TryFormat(destination[charsCurrentlyWritten..], out int tryFormatCharsWritten, null, provider);
- charsCurrentlyWritten += tryFormatCharsWritten;
- if (!tryFormatSucceeded || destination.Length < charsCurrentlyWritten + 1)
- {
- charsWritten = charsCurrentlyWritten;
- return false;
- }
-
- destination[charsCurrentlyWritten++] = ',';
- if (destination.Length < charsCurrentlyWritten + 1)
- {
- charsWritten = charsCurrentlyWritten;
- return false;
- }
- destination[charsCurrentlyWritten++] = ' ';
- if (destination.Length < charsCurrentlyWritten + 1)
- {
- charsWritten = charsCurrentlyWritten;
- return false;
- }
-
- tryFormatSucceeded = _imaginary.TryFormat(destination[charsCurrentlyWritten..], out tryFormatCharsWritten, null, provider);
- charsCurrentlyWritten += tryFormatCharsWritten;
- if (!tryFormatSucceeded || destination.Length < charsCurrentlyWritten + 1)
- {
- charsWritten = charsCurrentlyWritten;
- return false;
- }
-
- destination[charsCurrentlyWritten++] = ')';
-
- charsWritten = charsCurrentlyWritten;
- return true;
- }
- else if (format is "RE")
- {
- return _real.TryFormat(destination, out charsWritten, null, provider);
- }
- else if (format is "IM")
- {
- return _imaginary.TryFormat(destination, out charsWritten, null, provider);
- }
- else
- {
- throw new FormatException($"The \"{format}\" format is not supported.");
- }
- }
-
- //
- // Parsing
- //
-
- public static Complex Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider);
-
- public static Complex Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider);
-
- public static Complex Parse(string s, NumberStyles style, IFormatProvider? provider)
- {
- ArgumentNullException.ThrowIfNull(s);
- return Parse((ReadOnlySpan)s, style, provider);
- }
-
- public static Complex Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Float | NumberStyles.AllowThousands, IFormatProvider? provider = null)
- {
- if (!TryParse(s, style, provider, out Complex result))
- {
- return Infinity;
- }
- return result;
- }
-
- public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Complex result)
- => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out result);
-
- public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Complex result)
- => TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out result);
-
- public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Complex result)
- => TryParse((ReadOnlySpan)s, style, provider, out result);
-
- public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Complex result)
- {
- s = s.Trim();
- int openParenthesis = s.IndexOf('(');
- int split = s.IndexOf(',');
- int closeParenthesis = s.IndexOf(')');
-
- // There a minimum of 5 characters for "(0,0)".
- if (s.Length < 5 || openParenthesis == -1 || split == -1 || closeParenthesis == -1 || openParenthesis > split || openParenthesis > closeParenthesis || split > closeParenthesis)
- {
- result = Zero;
- return false;
- }
-
- if (!Real.TryParse(s.Slice(openParenthesis + 1, split - 1), style, provider, out Real real))
- {
- result = Zero;
- return false;
- }
-
- if (!Real.TryParse(s.Slice(split + 1, closeParenthesis - split - 1), style, provider, out Real imaginary))
- {
- result = Zero;
- return false;
- }
-
- result = new(real, imaginary);
- return true;
- }
-
- //
- // Methods
- //
-
- public static Complex Abs(Complex z) => Hypot(z._real.Value, z._imaginary.Value);
-
- public static Complex Conjugate(Complex z) => new(z._real, -z._imaginary);
-
- public static Complex FromPolarForm(Real magnitude, Real phase)
- => new(magnitude * T.Cos(phase.Value), magnitude * T.Sin(phase.Value));
-
- private static T Hypot(T x, T y)
- {
- // Factor out the larger value to avoid possible overflow
- x = T.Abs(x);
- y = T.Abs(y);
-
- T small, large;
- if (x < y)
- {
- small = x;
- large = y;
- }
- else
- {
- small = y;
- large = x;
- }
-
- if (small == T.Zero)
- {
- return large;
- }
- else if (T.IsPositiveInfinity(large) && !T.IsNaN(small))
- {
- return T.PositiveInfinity;
- }
- else
- {
- T ratio = small / large;
- return large * T.Sqrt(T.One + ratio * ratio);
- }
- }
-
- public static bool IsFinite(Complex z) => Real.IsFinite(z._real) && Real.IsFinite(z._imaginary);
-
- public static bool IsInfinity(Complex z) => Real.IsInfinity(z._real) || Real.IsInfinity(z._imaginary);
-
- public static bool IsNaN(Complex z) => !IsInfinity(z) && !IsFinite(z);
-
- public static bool IsZero(Complex z) => Real.IsZero(z._real) && Real.IsZero(z._imaginary);
-
- public static Complex Reciprocate(Complex z)
- {
- if (z._real == T.Zero && z._imaginary == T.Zero)
- {
- return Infinity;
- }
-
- var re = z._real.Value;
- var im = z._imaginary.Value;
-
- T reResult;
- T imResult;
- if (T.Abs(im) <= T.Abs(re))
- {
- DivisionHelper(T.One, T.Zero, re, im, out reResult, out imResult);
- }
- else
- {
- DivisionHelper(T.Zero, T.One, im, re, out reResult, out imResult);
- imResult = -imResult;
- }
-
- return new(reResult, imResult);
- }
-
- // We will only consider the real part of complex numbers for these conversions.
-
- public static bool TryConvertFromChecked(V value, out Complex result)
- where V : INumberBase
- {
- result = T.CreateChecked(value);
- return true;
- }
-
- public static bool TryConvertFromSaturating(V value, out Complex result)
- where V : INumberBase
- {
- result = T.CreateSaturating(value);
- return true;
- }
-
- public static bool TryConvertFromTruncating(V value, out Complex result)
- where V : INumberBase
- {
- result = T.CreateTruncating(value);
- return true;
- }
-
- public static bool TryConvertToChecked(Complex value, [MaybeNullWhen(false)] out V result)
- where V : INumberBase
- {
- if (value._imaginary == Real.Zero)
- {
- throw new OverflowException();
- }
-
- result = V.CreateChecked(value._real.Value);
- return true;
- }
-
- public static bool TryConvertToSaturating(Complex value, [MaybeNullWhen(false)] out V result)
- where V : INumberBase
- {
- result = V.CreateSaturating(value._real.Value);
- return true;
- }
-
- public static bool TryConvertToTruncating(Complex value, [MaybeNullWhen(false)] out V result)
- where V : INumberBase
- {
- result = V.CreateTruncating(value._real.Value);
- return true;
- }
-
- //
- // IDifferentiableFunctions interface
- //
-
- // Exponential functions
-
- public static Complex Exp(Complex z)
- {
- Real expReal = Real.Exp(z._real);
- return new(expReal * Real.Cos(z._imaginary), expReal * Real.Sin(z._imaginary));
- }
-
- public static Complex Exp2(Complex z) => Exp(Real.Ln2 * z);
-
- public static Complex Exp10(Complex z) => Exp(Real.Ln10 * z);
-
- // Hyperbolic functions
-
- public static Complex Acosh(Complex z) => Ln(z + Sqrt(z * z - One));
-
- public static Complex Asinh(Complex z) => Ln(z + Sqrt(z * z + One));
-
- public static Complex Atanh(Complex z) => s_oneOverTwo * Ln((One + z) / (One - z));
-
- public static Complex Cosh(Complex z)
- => new(Real.Cosh(z._real) * Real.Cos(z._imaginary), Real.Sinh(z._real) * Real.Sin(z._imaginary));
-
- public static Complex Sinh(Complex z)
- => new(Real.Sinh(z._real) * Real.Cos(z._imaginary), Real.Cosh(z._real) * Real.Sin(z._imaginary));
-
- public static Complex Tanh(Complex z) => Sinh(z) / Cosh(z);
-
- // Logarithmic functions
-
- public static Complex Ln(Complex z) => new(Real.Ln(Hypot(z._real.Value, z._imaginary.Value)), Real.Atan2(z._imaginary, z._real));
-
- public static Complex Log(Complex z, Complex b) => Ln(z) / Ln(b);
-
- public static Complex Log2(Complex z) => Ln(z) / Ln(Real.Ln2);
-
- public static Complex Log10(Complex z) => Ln(z) / Ln(Real.Ln10);
-
- // Power functions
-
- public static Complex Pow(Complex z, Complex w) => Exp(w * Ln(z));
-
- // Root functions
-
- public static Complex Cbrt(Complex z) => Exp(Ln(z) / s_three);
-
- public static Complex Root(Complex z, Complex w) => Exp(Ln(z) / w);
-
- public static Complex Sqrt(Complex z) => Exp(s_oneOverTwo * Ln(z));
-
- // Trigonometric functions
-
- public static Complex Acos(Complex z)
- {
- AsinInternal(Real.Abs(z._real), Real.Abs(z._imaginary), out Real b, out Real bPrime, out Real v);
-
- Real u;
- if (bPrime < Real.Zero)
- {
- u = Real.Acos(b);
- }
- else
- {
- u = Real.Atan(Real.One / bPrime);
- }
-
- if (z._real < Real.Zero)
- {
- u = Real.Pi - u;
- }
- if (z._imaginary > Real.Zero)
- {
- v = -v;
- }
-
- return new(u, v);
- }
-
- public static Complex Asin(Complex z)
- {
- AsinInternal(Real.Abs(z._real), Real.Abs(z._imaginary), out Real b, out Real bPrime, out Real v);
-
- Real u;
- if (bPrime < Real.Zero)
- {
- u = Real.Asin(b);
- }
- else
- {
- u = Real.Atan(bPrime);
- }
-
- if (z._real < Real.Zero)
- {
- u = -u;
- }
- if (z._imaginary < Real.Zero)
- {
- v = -v;
- }
-
- return new(u, v);
- }
-
- private static void AsinInternal(Real x, Real y, out Real b, out Real bPrime, out Real v)
- {
- // This is the same method described by Hull, Fairgrieve, and Tang in "Implementing the Complex
- // ArcSine and Arccosine Functions Using Exception Handling" that is used in System.Numerics.Complex.
- if (x > s_asinOverflowThreshold || y > s_asinOverflowThreshold)
- {
- b = -Real