diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml
index ac6c7f661..77d45317b 100644
--- a/.github/workflows/Build.yml
+++ b/.github/workflows/Build.yml
@@ -28,4 +28,8 @@ jobs:
with:
dotnet-version: '5.0.100'
- name: Build
- run: dotnet build || dotnet build || dotnet build
\ No newline at end of file
+ run: |
+ dotnet build AngouriMath/AngouriMath.csproj
+ dotnet build FSharp/AngouriMath.FSharp.fsproj
+ dotnet build Analyzers/Analyzers/Analyzers.csproj
+ dotnet build Utils/Utils/Utils.csproj
\ No newline at end of file
diff --git a/Analyzers/Analyzers.CodeFixes/Analyzers.CodeFixes.csproj b/Analyzers/Analyzers.CodeFixes/Analyzers.CodeFixes.csproj
new file mode 100644
index 000000000..cd29b996b
--- /dev/null
+++ b/Analyzers/Analyzers.CodeFixes/Analyzers.CodeFixes.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netstandard2.0
+ false
+ Analyzers
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Analyzers/Analyzers.CodeFixes/FieldThreadSafetyCodeFixProvider.cs b/Analyzers/Analyzers.CodeFixes/FieldThreadSafetyCodeFixProvider.cs
new file mode 100644
index 000000000..0a3f5cad4
--- /dev/null
+++ b/Analyzers/Analyzers.CodeFixes/FieldThreadSafetyCodeFixProvider.cs
@@ -0,0 +1,40 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Rename;
+using Microsoft.CodeAnalysis.Text;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Analyzers
+{
+ [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FieldThreadSafetyCodeFixProvider)), Shared]
+ public class FieldThreadSafetyCodeFixProvider : CodeFixProvider
+ {
+ public sealed override ImmutableArray FixableDiagnosticIds
+ {
+ get { return ImmutableArray.Create(EitherAbstractOrSealed.DiagnosticId); }
+ }
+
+ public sealed override FixAllProvider GetFixAllProvider()
+ {
+ // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
+ return WellKnownFixAllProviders.BatchFixer;
+ }
+
+#pragma warning disable CS1998 // To be implemented yet
+ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
+#pragma warning restore CS1998
+ {
+
+ }
+
+ }
+}
diff --git a/Analyzers/Analyzers.Debug/Analyzers.Debug.csproj b/Analyzers/Analyzers.Debug/Analyzers.Debug.csproj
new file mode 100644
index 000000000..3cba0ec3d
--- /dev/null
+++ b/Analyzers/Analyzers.Debug/Analyzers.Debug.csproj
@@ -0,0 +1,48 @@
+
+
+
+
+
+ net472
+ Analyzers.Debug
+ Analyzers.Debug
+ win
+
+
+
+ false
+ false
+ false
+ false
+ false
+ false
+ Roslyn
+
+
+
+
+
+
+
+ Program
+ $(DevEnvDir)devenv.exe
+ /rootsuffix $(VSSDKTargetPlatformRegRootSuffix)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Analyzers/Analyzers.Debug/source.extension.vsixmanifest b/Analyzers/Analyzers.Debug/source.extension.vsixmanifest
new file mode 100644
index 000000000..72804275c
--- /dev/null
+++ b/Analyzers/Analyzers.Debug/source.extension.vsixmanifest
@@ -0,0 +1,22 @@
+
+
+
+
+ Analyzers
+ This extension allows to easily debug analyzers for AngouriMath
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Analyzers/Analyzers/Analyzers.csproj b/Analyzers/Analyzers/Analyzers.csproj
new file mode 100644
index 000000000..a6dde8729
--- /dev/null
+++ b/Analyzers/Analyzers/Analyzers.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netstandard2.0
+ false
+
+
+ *$(MSBuildProjectFullPath)*
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/Analyzers/Analyzers/EitherAbstractOrSealed.cs b/Analyzers/Analyzers/EitherAbstractOrSealed.cs
new file mode 100644
index 000000000..ad6c128c5
--- /dev/null
+++ b/Analyzers/Analyzers/EitherAbstractOrSealed.cs
@@ -0,0 +1,45 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+
+namespace Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class EitherAbstractOrSealed : DiagnosticAnalyzer
+ {
+ public const string DiagnosticId = "SealedOrAbstract";
+ private static readonly string Title = "AMAnalyzer";
+ private static readonly string MessageFormat = $"If a type is not sealed, it should be either static or abstract";
+ private static readonly string Description = "Unsafe type extension prevention.";
+ private const string Category = "Security";
+
+ private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
+
+ public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterSymbolAction(symbolContext =>
+ {
+ var typeDecl = (INamedTypeSymbol)symbolContext.Symbol;
+ var containingType = typeDecl.ContainingType;
+ if (!typeDecl.IsAbstract && !typeDecl.IsSealed && !typeDecl.IsStatic)
+ {
+ var diag = Diagnostic.Create(Rule, typeDecl.Locations.First());
+ symbolContext.ReportDiagnostic(diag);
+ }
+ }, SymbolKind.NamedType);
+ }
+ }
+}
diff --git a/Analyzers/Analyzers/GlobalSuppressions.cs b/Analyzers/Analyzers/GlobalSuppressions.cs
new file mode 100644
index 000000000..0d27ec25a
--- /dev/null
+++ b/Analyzers/Analyzers/GlobalSuppressions.cs
@@ -0,0 +1,13 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+// Most warnings about shipping are because we are not planning to ship
+// this analyzer, it is exclusively for contributors of the project.
+
+[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking", Justification = "", Scope = "member", Target = "~F:Analyzers.StaticFieldThreadSafety.RuleShouldBeNull")]
+[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking", Justification = "", Scope = "member", Target = "~F:Analyzers.StaticFieldThreadSafety.RuleAddAttribute")]
+[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking", Justification = "", Scope = "member", Target = "~F:Analyzers.EitherAbstractOrSealed.Rule")]
diff --git a/Analyzers/Analyzers/StaticFieldThreadSafety.cs b/Analyzers/Analyzers/StaticFieldThreadSafety.cs
new file mode 100644
index 000000000..53d7914ea
--- /dev/null
+++ b/Analyzers/Analyzers/StaticFieldThreadSafety.cs
@@ -0,0 +1,80 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class StaticFieldThreadSafety : DiagnosticAnalyzer
+ {
+ public const string DiagnosticId = "ThreadSafety";
+
+ private static readonly DiagnosticDescriptor RuleAddAttribute =
+ new DiagnosticDescriptor(
+ id: DiagnosticId,
+ title: "AMAnalyzer",
+ messageFormat: "A static field should have either [ConstantField] attribute or [ThreadStatic] (for cache)",
+ category: "Security",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "Data corruption prevention.");
+
+ private static readonly DiagnosticDescriptor RuleShouldBeNull =
+ new DiagnosticDescriptor(
+ id: DiagnosticId,
+ title: "AMAnalyzer",
+ messageFormat: "A thread static field should have no initialization (instead, should have an additional nullable backing field)",
+ category: "Security",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "Non-initialized field prevention.");
+
+ public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(RuleAddAttribute); } }
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterSymbolAction(symbolContext =>
+ {
+ var fieldDecl = (IFieldSymbol)symbolContext.Symbol;
+ var hasConstantFieldAttribute = fieldDecl.GetAttributes().Any(attr => attr.AttributeClass?.Name == "ConstantFieldAttribute");
+ var hasThreadStaticAttribute = fieldDecl.GetAttributes().Any(attr => attr.AttributeClass?.Name == "ThreadStaticAttribute");
+ if (fieldDecl.IsStatic &&
+ !fieldDecl.IsConst &&
+ !hasThreadStaticAttribute
+ &&
+ !hasConstantFieldAttribute
+ &&
+ fieldDecl.Type.Name != "ConditionalWeakTable"
+ )
+ {
+ var diag = Diagnostic.Create(RuleAddAttribute, fieldDecl.Locations.First());
+ symbolContext.ReportDiagnostic(diag);
+ }
+ if (hasThreadStaticAttribute)
+ {
+ var syntaxTree = fieldDecl.DeclaringSyntaxReferences.First().GetSyntax();
+ if (syntaxTree is VariableDeclaratorSyntax variableDeclarator)
+ {
+ if (variableDeclarator.Initializer is not null)
+ {
+ var diag = Diagnostic.Create(RuleShouldBeNull, fieldDecl.Locations.First());
+#pragma warning disable RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor
+ symbolContext.ReportDiagnostic(diag);
+#pragma warning restore RS1005 // ReportDiagnostic invoked with an unsupported DiagnosticDescriptor
+ }
+ }
+ }
+ }, SymbolKind.Field);
+ }
+ }
+}
diff --git a/AngouriMath.sln b/AngouriMath.sln
index 4e6a49d13..2b16cf6a7 100644
--- a/AngouriMath.sln
+++ b/AngouriMath.sln
@@ -40,6 +40,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleNet5", "Samples\Sampl
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharpSample", "Samples\FSharpSample\FSharpSample.fsproj", "{E1848158-0D50-4762-BC06-DAC5DF47DCB2}"
EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "AngouriMath.FSharp", "FSharp\AngouriMath.FSharp.fsproj", "{4EB32057-7AD8-4C58-A25C-A666ACBDAA12}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Analyzers", "Analyzers\Analyzers\Analyzers.csproj", "{A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Analyzers.CodeFixes", "Analyzers\Analyzers.CodeFixes\Analyzers.CodeFixes.csproj", "{DBE2D86F-482E-418F-9A6B-298B4EF1DB27}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{7B0F7A1A-7C84-4B17-85D0-24BBCAA18826}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Analyzers.Debug", "Analyzers\Analyzers.Debug\Analyzers.Debug.csproj", "{DAE7E49D-A474-4776-91CF-3368E9C56F3D}"
+EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "AngouriMath.FSharp", "Wrappers\FSharp\AngouriMath.FSharp.fsproj", "{4EB32057-7AD8-4C58-A25C-A666ACBDAA12}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Wrappers", "Wrappers", "{C82FFADF-2F2F-4B8D-BB32-2D2F149FED4D}"
@@ -134,6 +144,30 @@ Global
{4EB32057-7AD8-4C58-A25C-A666ACBDAA12}.Release|Any CPU.Build.0 = Release|Any CPU
{4EB32057-7AD8-4C58-A25C-A666ACBDAA12}.Release|x64.ActiveCfg = Release|Any CPU
{4EB32057-7AD8-4C58-A25C-A666ACBDAA12}.Release|x64.Build.0 = Release|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Debug|x64.Build.0 = Debug|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Release|x64.ActiveCfg = Release|Any CPU
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5}.Release|x64.Build.0 = Release|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Debug|x64.Build.0 = Debug|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Release|x64.ActiveCfg = Release|Any CPU
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27}.Release|x64.Build.0 = Release|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Debug|x64.Build.0 = Debug|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Release|x64.ActiveCfg = Release|Any CPU
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D}.Release|x64.Build.0 = Release|Any CPU
{13618971-EDBB-4C64-A0FF-F66AE17778E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13618971-EDBB-4C64-A0FF-F66AE17778E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13618971-EDBB-4C64-A0FF-F66AE17778E3}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -154,6 +188,9 @@ Global
{B7D28355-A342-4E6E-AD45-3D47F4A054A5} = {D15528F2-AB50-46E2-9DB1-821B278C4931}
{0116746A-3B68-4B75-A926-D9B8E7E87571} = {D15528F2-AB50-46E2-9DB1-821B278C4931}
{E1848158-0D50-4762-BC06-DAC5DF47DCB2} = {D15528F2-AB50-46E2-9DB1-821B278C4931}
+ {A37A22C6-40C6-4B11-BCF4-65A61CBD4AD5} = {7B0F7A1A-7C84-4B17-85D0-24BBCAA18826}
+ {DBE2D86F-482E-418F-9A6B-298B4EF1DB27} = {7B0F7A1A-7C84-4B17-85D0-24BBCAA18826}
+ {DAE7E49D-A474-4776-91CF-3368E9C56F3D} = {7B0F7A1A-7C84-4B17-85D0-24BBCAA18826}
{4EB32057-7AD8-4C58-A25C-A666ACBDAA12} = {C82FFADF-2F2F-4B8D-BB32-2D2F149FED4D}
{13618971-EDBB-4C64-A0FF-F66AE17778E3} = {C82FFADF-2F2F-4B8D-BB32-2D2F149FED4D}
EndGlobalSection
diff --git a/AngouriMath/AngouriMath.csproj b/AngouriMath/AngouriMath.csproj
index f3df4bd01..1f21f6227 100644
--- a/AngouriMath/AngouriMath.csproj
+++ b/AngouriMath/AngouriMath.csproj
@@ -43,5 +43,9 @@ Preview 3: Derive -> Differentiate, now all calculus functions apply InnerSim
+
\ No newline at end of file
diff --git a/AngouriMath/Convenience/MathS.cs b/AngouriMath/Convenience/MathS.cs
index 29a25f0ae..dd16320e2 100644
--- a/AngouriMath/Convenience/MathS.cs
+++ b/AngouriMath/Convenience/MathS.cs
@@ -17,6 +17,7 @@
using AngouriMath.Functions.Algebra;
using AngouriMath.Functions.Boolean;
using System.Diagnostics.CodeAnalysis;
+using AngouriMath.Convenience;
namespace AngouriMath.Core
{
@@ -401,28 +402,28 @@ public static class TrigonometricHyperpolic
/// The e constant
///
///
- public static readonly Variable e = Variable.e;
+ [ConstantField] public static readonly Variable e = Variable.e;
// ReSharper disable once InconsistentNaming
///
/// The imaginary one
///
///
- public static readonly Complex i = Complex.ImaginaryOne;
+ [ConstantField] public static readonly Complex i = Complex.ImaginaryOne;
// ReSharper disable once InconsistentNaming
///
/// The pi constant
///
///
- public static readonly Variable pi = Variable.pi;
+ [ConstantField] public static readonly Variable pi = Variable.pi;
// Undefined
///
/// That is both undefined and indeterminite
/// Any operation on NaN returns NaN
///
- public static readonly Entity NaN = Real.NaN;
+ [ConstantField] public static readonly Entity NaN = Real.NaN;
/// Converts a to an expression
/// expression, for example, "2 * x + 3 + sqrt(x)"
@@ -617,88 +618,6 @@ public static Tensor Matrix(int rows, int columns, params Entity[] values) =>
///
public static partial class Settings
{
- ///
- /// This class for configuring some internal mechanisms from outside
- ///
- ///
- /// Those configurations can be of different types
- ///
- public sealed class Setting where T : notnull
- {
- internal Setting(T defaultValue) { Value = defaultValue; Default = defaultValue; }
-
- ///
- /// For example,
- ///
- /// MathS.Settings.Precision.As(100, () => { /* some code considering precision = 100 */ });
- ///
- ///
- /// New value that will be automatically reverted after action is done
- /// What should be done under this setting
- public void As(T value, Action action)
- {
- var previousValue = Value;
- Value = value;
- try
- {
- action();
- }
- finally
- {
- Value = previousValue;
- }
- }
-
- ///
- /// For example,
- ///
- /// var res = MathS.Settings.Precision.As(100, () => { /* some code considering precision = 100 */ return 4; });
- ///
- ///
- /// New value that will be automatically reverted after action is done
- /// What should be done under this setting
- public TReturnType As(T value, Func action)
- {
- var previousValue = Value;
- Value = value;
- try
- {
- return action();
- }
- finally
- {
- Value = previousValue;
- }
- }
-
- ///
- /// An implicit operator so that one does not have to call
- ///
- /// The setting
- public static implicit operator T(Setting s) => s.Value;
-
- ///
- /// An implicit operator so that one does not have to call the ctor
- ///
- /// The value
- public static implicit operator Setting(T a) => new(a);
-
- ///
- /// Overriden ToString so that one could see the value of the setting
- /// (if overriden)
- ///
- public override string ToString() => Value.ToString();
-
- ///
- /// The current value of the setting
- ///
- public T Value { get; private set; }
-
- ///
- /// The default value of the setting
- ///
- public T Default { get; }
- }
///
/// That is how we perform newton solving when no analytical solution was found
@@ -771,30 +690,40 @@ public sealed record NewtonSetting
///
/// Criteria for simplifier so you could control which expressions are considered easier by you
///
- public static Setting> ComplexityCriteria =>
- complexityCriteria ??= new Func(expr =>
+ public static Setting> ComplexityCriteria =>
+ complexityCriteria ??= new Func(expr =>
{
+ // Those are of the 2nd power to avoid problems with floating numbers
+ static double TinyWeight(double w) => w * 0.5;
+ static double MinorWeight(double w) => w * 1.0;
+ static double Weight(double w) => w * 2.0;
+ static double MajorWeight(double w) => w * 4.0;
+ static double HeavyWeight(double w) => w * 8.0;
+
// Number of nodes
- var res = expr.Complexity * 2;
+ var res = Weight(expr.Complexity);
// Number of variables
- res += expr.Nodes.Count(entity => entity is Variable) * 2;
+ res += Weight(expr.Nodes.Count(entity => entity is Variable));
// Number of divides
- res += expr.Nodes.Count(entity => entity is Divf);
+ res += MinorWeight(expr.Nodes.Count(entity => entity is Divf));
// Number of negative powers
- res += expr.Nodes.Count(entity => entity is Powf(_, Real { IsNegative: true })) * 8;
+ res += HeavyWeight(expr.Nodes.Count(entity => entity is Powf(_, Real { IsNegative: true })));
+
+ // Number of logarithms
+ res += TinyWeight(expr.Nodes.Count(entity => entity is Logf));
// Number of negative reals
- res += expr.Nodes.Count(entity => entity is Real { IsNegative: true }) * 6 /* to outweigh number of nodes */;
+ res += MajorWeight(expr.Nodes.Count(entity => entity is Real { IsNegative: true }));
// 0 < x is bad. x > 0 is good.
- res += expr.Nodes.Count(entity => entity is ComparisonSign && entity.DirectChildren[0] == 0) * 2;
+ res += Weight(expr.Nodes.Count(entity => entity is ComparisonSign && entity.DirectChildren[0] == 0));
return res;
});
- [ThreadStatic] private static Setting>? complexityCriteria;
+ [ThreadStatic] private static Setting>? complexityCriteria;
///
/// Settings for the Newton-Raphson's root-search method
@@ -1192,7 +1121,7 @@ public static class Unsafe
/// instead and set the flag useCache to false)
///
public static void ClearFromStringCache()
- => MathS.stringToEntityCache = new();
+ => stringToEntityCache = new();
///
/// Entities' properties are not initialized once
@@ -1201,7 +1130,7 @@ public static void ClearFromStringCache()
/// be critical for some system. You can clean it
///
public static void ClearEntityPropertyCache()
- => Entity.caches = new();
+ => caches = new();
}
///
@@ -1234,7 +1163,7 @@ public static bool AreEqualNumerically(Entity expr1, Entity expr2)
-100, -10, 1, 10, 100, 1.5,
"-100 + i", "-10 + 2i", "30i"
};
- private static Setting? checkPoints;
+ [ConstantField] private static Setting? checkPoints;
}
}
}
diff --git a/AngouriMath/Convenience/SettingClass.cs b/AngouriMath/Convenience/SettingClass.cs
new file mode 100644
index 000000000..2b2857d1a
--- /dev/null
+++ b/AngouriMath/Convenience/SettingClass.cs
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019-2020 Angourisoft
+ *
+ * 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;
+
+namespace AngouriMath.Convenience
+{
+ ///
+ /// This class for configuring some internal mechanisms from outside
+ ///
+ ///
+ /// Those configurations can be of different types
+ ///
+ public sealed class Setting where T : notnull
+ {
+ internal Setting(T defaultValue) { Value = defaultValue; Default = defaultValue; }
+
+ ///
+ /// For example,
+ ///
+ /// MathS.Settings.Precision.As(100, () => { /* some code considering precision = 100 */ });
+ ///
+ ///
+ /// New value that will be automatically reverted after action is done
+ /// What should be done under this setting
+ public void As(T value, Action action)
+ {
+ var previousValue = Value;
+ Value = value;
+ try
+ {
+ action();
+ }
+ finally
+ {
+ Value = previousValue;
+ }
+ }
+
+ ///
+ /// Use it if you don't need to unset it
+ /// Should be NOT used for industrial projects
+ /// (only for some manual calculations)
+ ///
+ /// The new value of the setting
+ public void Global(T value)
+ => Value = value;
+
+ ///
+ /// Use it in case if you need to cancel any adjustments of the setting
+ /// and rollback to the one predefined in .
+ /// It does not roll back to the previous value. Instead, it sets the
+ /// value which is defined right after is
+ /// initialized. If you need recoverable settings (aka local),
+ /// use method .
+ ///
+ public void RollBackToDefault()
+ => Global(Default);
+
+ ///
+ /// For example,
+ ///
+ /// var res = MathS.Settings.Precision.As(100, () => { /* some code considering precision = 100 */ return 4; });
+ ///
+ ///
+ /// New value that will be automatically reverted after action is done
+ /// What should be done under this setting
+ public TReturnType As(T value, Func action)
+ {
+ var previousValue = Value;
+ Value = value;
+ try
+ {
+ return action();
+ }
+ finally
+ {
+ Value = previousValue;
+ }
+ }
+
+ ///
+ /// An implicit operator so that one does not have to call
+ ///
+ /// The setting
+ public static implicit operator T(Setting s) => s.Value;
+
+ ///
+ /// An implicit operator so that one does not have to call the ctor
+ ///
+ /// The value
+ public static implicit operator Setting(T a) => new(a);
+
+ ///
+ /// Overriden ToString so that one could see the value of the setting
+ /// (if overriden)
+ ///
+ public override string ToString() => Value.ToString();
+
+ ///
+ /// The current value of the setting
+ ///
+ public T Value { get; private set; }
+
+ ///
+ /// The default value of the setting
+ ///
+ public T Default { get; }
+ }
+}
diff --git a/AngouriMath/Core/Compilation/Compiler.cs b/AngouriMath/Core/Compilation/Compiler.cs
index 5aeee05e5..5b3e85f6a 100644
--- a/AngouriMath/Core/Compilation/Compiler.cs
+++ b/AngouriMath/Core/Compilation/Compiler.cs
@@ -289,7 +289,7 @@ public partial class FastExpression
{
/// The stores the saved cache number if zero/positive,
/// or the bitwise complement of the unsaved cache number if negative.
- internal record Compiler(List Instructions,
+ internal sealed record Compiler(List Instructions,
IReadOnlyDictionary VarNamespace, IDictionary Cache)
{
/// Returns a compiled expression. Allows to boost substitution a lot
diff --git a/AngouriMath/Core/Compilation/FastExpression.cs b/AngouriMath/Core/Compilation/FastExpression.cs
index 37b397694..a4ee89ac2 100644
--- a/AngouriMath/Core/Compilation/FastExpression.cs
+++ b/AngouriMath/Core/Compilation/FastExpression.cs
@@ -45,7 +45,7 @@ public FastExpression Compile(params string[] variables) =>
}
namespace AngouriMath.Core
{
- public partial class FastExpression
+ public sealed partial class FastExpression
{
internal enum InstructionType
{
@@ -79,7 +79,7 @@ internal enum InstructionType
CALL_POW,
CALL_LOG,
}
- internal partial record Instruction(InstructionType Type, int Reference = -1, Complex Value = default)
+ internal sealed partial record Instruction(InstructionType Type, int Reference = -1, Complex Value = default)
{
public override string ToString() =>
Type
@@ -219,7 +219,7 @@ static Complex Gamma(Complex z)
return stack.Pop();
}
- static readonly double[] gammaCoeffs = {
+ [ConstantField] static readonly double[] gammaCoeffs = {
0.99999999999980993, 676.5203681218851, -1259.1392167224028,
771.32342877765313, -176.61502916214059, 12.507343278686905,
-0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7
diff --git a/AngouriMath/Core/CoreAttributes.cs b/AngouriMath/Core/CoreAttributes.cs
new file mode 100644
index 000000000..f8382b181
--- /dev/null
+++ b/AngouriMath/Core/CoreAttributes.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AngouriMath.Core
+{
+ ///
+ /// Use this attribute on those static fields that do not require thread static attribute
+ /// because they are constant
+ ///
+ [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
+ internal sealed class ConstantFieldAttribute : Attribute
+ {
+
+ }
+}
diff --git a/AngouriMath/Core/Domains.Classes.cs b/AngouriMath/Core/Domains.Classes.cs
index 0110989a1..ca99faac5 100644
--- a/AngouriMath/Core/Domains.Classes.cs
+++ b/AngouriMath/Core/Domains.Classes.cs
@@ -15,6 +15,7 @@ partial record Entity
{
partial record Number
{
+#pragma warning disable SealedOrAbstract // The only exception: those three records are neither abstract nor sealed
partial record Complex
{
///
@@ -32,6 +33,7 @@ partial record Real
///
public override Domain Codomain { get; protected init; } = Domain.Real;
}
+#pragma warning restore SealedOrAbstract
partial record Integer
{
diff --git a/AngouriMath/Core/Domains.cs b/AngouriMath/Core/Domains.cs
index 0ac7a516f..59229235b 100644
--- a/AngouriMath/Core/Domains.cs
+++ b/AngouriMath/Core/Domains.cs
@@ -50,14 +50,6 @@ public enum Domain
internal static class DomainsFunctional
{
- public static readonly Type[] Types = new[]
- {
- typeof(Entity.Boolean),
- typeof(Entity.Number.Integer),
- typeof(Entity.Number.Rational),
- typeof(Entity.Number.Real),
- typeof(Entity.Number.Complex)
- };
public static bool FitsDomainOrNonNumeric(Entity entity, Domain domain)
=> domain == Domain.Any || SpecialSet.Create(domain).MayContain(entity);
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs
index bbe996791..82a352709 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs
@@ -18,7 +18,7 @@ partial record Entity
///
/// A node of signum
///
- public partial record Signumf(Entity Argument) : Function
+ public sealed partial record Signumf(Entity Argument) : Function
{
private Signumf New(Entity arg) =>
ReferenceEquals(Argument, arg) ? this : new(arg);
@@ -31,7 +31,7 @@ private Signumf New(Entity arg) =>
///
/// A node of abs
///
- public partial record Absf(Entity Argument) : Function
+ public sealed partial record Absf(Entity Argument) : Function
{
private Absf New(Entity arg) =>
ReferenceEquals(Argument, arg) ? this : new(arg);
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs
index dfbdc0326..0552606a8 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs
@@ -18,7 +18,7 @@ partial record Entity
///
/// A node of arcsine
///
- public partial record Arcsinf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Arcsinf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Arcsinf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -31,7 +31,7 @@ public partial record Arcsinf(Entity Argument) : TrigonometricFunction
///
/// A node of arccosine
///
- public partial record Arccosf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Arccosf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Arccosf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -44,7 +44,7 @@ public partial record Arccosf(Entity Argument) : TrigonometricFunction
///
/// A node of arctangent
///
- public partial record Arctanf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Arctanf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Arctanf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -57,7 +57,7 @@ public partial record Arctanf(Entity Argument) : TrigonometricFunction
///
/// A node of arccotangent
///
- public partial record Arccotanf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Arccotanf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
Arccotanf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -70,7 +70,7 @@ public partial record Arccotanf(Entity Argument) : TrigonometricFunction
///
/// A node of arcsecant
///
- public partial record Arcsecantf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Arcsecantf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Arcsecantf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -83,7 +83,7 @@ public partial record Arcsecantf(Entity Argument) : TrigonometricFunction
///
/// A node of arccosecant
///
- public partial record Arccosecantf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Arccosecantf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Arccosecantf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs
index c69941cf1..ebbda4620 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs
@@ -20,7 +20,7 @@ partial record Entity
///
/// A node of derivative
///
- public partial record Derivativef(Entity Expression, Entity Var, int Iterations) : Function
+ public sealed partial record Derivativef(Entity Expression, Entity Var, int Iterations) : Function
{
/// Reuse the cache by returning the same object if possible
private Derivativef New(Entity expression, Entity var) =>
@@ -36,7 +36,7 @@ public override Entity Replace(Func func) =>
///
/// A node of integral
///
- public partial record Integralf(Entity Expression, Entity Var, int Iterations) : Function
+ public sealed partial record Integralf(Entity Expression, Entity Var, int Iterations) : Function
{
/// Reuse the cache by returning the same object if possible
private Integralf New(Entity expression, Entity var) =>
@@ -52,7 +52,7 @@ public override Entity Replace(Func func) =>
///
/// A node of limit
///
- public partial record Limitf(Entity Expression, Entity Var, Entity Destination, ApproachFrom ApproachFrom) : Function
+ public sealed partial record Limitf(Entity Expression, Entity Var, Entity Destination, ApproachFrom ApproachFrom) : Function
{
/// Reuse the cache by returning the same object if possible
private Limitf New(Entity expression, Entity var, Entity destination, ApproachFrom approachFrom) =>
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs
index 240a158b9..87ecb91aa 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs
@@ -50,12 +50,12 @@ private protected Complex(Real? real, Real? imaginary) =>
///
/// An imaginary one. You can use it to avoid allocations
///
- public static readonly Complex ImaginaryOne = new Complex(0, 1);
+ [ConstantField] public static readonly Complex ImaginaryOne = new Complex(0, 1);
///
/// An imaginary minus one. You can use it to avoid allocations
///
- public static readonly Complex MinusImaginaryOne = new Complex(0, -1);
+ [ConstantField] public static readonly Complex MinusImaginaryOne = new Complex(0, -1);
///
protected override bool ThisIsFinite => RealPart.EDecimal.IsFinite && ImaginaryPart.EDecimal.IsFinite;
@@ -127,19 +127,19 @@ public static Complex CreatePolar(EDecimal magnitude, EDecimal phase)
}
/// -oo + -ooi
- public static readonly Complex NegNegInfinity =
+ [ConstantField] public static readonly Complex NegNegInfinity =
new Complex(Real.NegativeInfinity, Real.NegativeInfinity);
/// -oo + +ooi
- public static readonly Complex NegPosInfinity =
+ [ConstantField] public static readonly Complex NegPosInfinity =
new Complex(Real.NegativeInfinity, Real.PositiveInfinity);
/// +oo + -ooi
- public static readonly Complex PosNegInfinity =
+ [ConstantField] public static readonly Complex PosNegInfinity =
new Complex(Real.PositiveInfinity, Real.NegativeInfinity);
/// +oo + +ooi
- public static readonly Complex PosPosInfinity =
+ [ConstantField] public static readonly Complex PosPosInfinity =
new Complex(Real.PositiveInfinity, Real.PositiveInfinity);
/// Parses a into
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Definition.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Definition.cs
index 46845ed9e..def97712d 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Definition.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Definition.cs
@@ -108,8 +108,10 @@ public static implicit operator Entity(Complex value) =>
///
public Entity Arccosec() => new Arccosecantf(this);
///
+ /// Power
public Entity Pow(Entity n) => new Powf(this, n);
///
+ /// Antilogarithm (value)
public Entity Log(Entity x) => new Logf(this, x);
///
public Entity Arcsin() => new Arcsinf(this);
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs
index 0cac547cb..1b9249df7 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs
@@ -19,7 +19,7 @@ partial record Entity
///
/// A node of exponential (power)
///
- public partial record Powf(Entity Base, Entity Exponent) : Entity
+ public sealed partial record Powf(Entity Base, Entity Exponent) : Entity
{
/// Reuse the cache by returning the same object if possible
private Powf New(Entity @base, Entity exponent) =>
@@ -34,7 +34,7 @@ private Powf New(Entity @base, Entity exponent) =>
///
/// A node of logarithm
///
- public partial record Logf(Entity Base, Entity Antilogarithm) : Function
+ public sealed partial record Logf(Entity Base, Entity Antilogarithm) : Function
{
/// Reuse the cache by returning the same object if possible
private Logf New(Entity @base, Entity antilogarithm) =>
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs
index 88e27022a..a8efc9a29 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs
@@ -19,7 +19,7 @@ partial record Entity
///
/// A node of factorial
///
- public partial record Factorialf(Entity Argument) : Function
+ public sealed partial record Factorialf(Entity Argument) : Function
{
/// Reuse the cache by returning the same object if possible
private Factorialf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs
index ae117fb28..6af553d55 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs
@@ -37,17 +37,17 @@ public sealed partial record Integer : Rational, System.IComparable
///
/// A zero, you can use it to avoid allocations
///
- public static readonly Integer Zero = new Integer(EInteger.Zero);
+ [ConstantField] public static readonly Integer Zero = new Integer(EInteger.Zero);
///
/// A one, you can use it to avoid allocations
///
- public static readonly Integer One = new Integer(EInteger.One);
+ [ConstantField] public static readonly Integer One = new Integer(EInteger.One);
///
/// A minus one, you can use it to avoid allocations
///
- public static readonly Integer MinusOne = new Integer(-EInteger.One);
+ [ConstantField] public static readonly Integer MinusOne = new Integer(-EInteger.One);
///
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs
index 01caeffba..6aa669ae0 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs
@@ -21,7 +21,7 @@ partial record Entity
///
/// A node of sum
///
- public partial record Sumf(Entity Augend, Entity Addend) : NumericNode
+ public sealed partial record Sumf(Entity Augend, Entity Addend) : NumericNode
{
/// Reuse the cache by returning the same object if possible
private Sumf New(Entity augend, Entity addend) =>
@@ -49,7 +49,7 @@ private Sumf New(Entity augend, Entity addend) =>
///
/// A node of difference
///
- public partial record Minusf(Entity Subtrahend, Entity Minuend) : NumericNode
+ public sealed partial record Minusf(Entity Subtrahend, Entity Minuend) : NumericNode
{
/// Reuse the cache by returning the same object if possible
private Minusf New(Entity subtrahend, Entity minuend) =>
@@ -64,7 +64,7 @@ private Minusf New(Entity subtrahend, Entity minuend) =>
///
/// A node of product
///
- public partial record Mulf(Entity Multiplier, Entity Multiplicand) : NumericNode
+ public sealed partial record Mulf(Entity Multiplier, Entity Multiplicand) : NumericNode
{
/// Reuse the cache by returning the same object if possible
private Mulf New(Entity multiplier, Entity multiplicand) =>
@@ -92,7 +92,7 @@ private Mulf New(Entity multiplier, Entity multiplicand) =>
///
/// A node of division
///
- public partial record Divf(Entity Dividend, Entity Divisor) : NumericNode
+ public sealed partial record Divf(Entity Dividend, Entity Divisor) : NumericNode
{
/// Reuse the cache by returning the same object if possible
private Divf New(Entity dividend, Entity divisor) =>
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs
index f08d60092..26c1fc288 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs
@@ -97,13 +97,13 @@ internal static bool TryParse(string s,
}
/// Negative Infinity (-oo)
- public static readonly Real NegativeInfinity = new Real(EDecimal.NegativeInfinity);
+ [ConstantField] public static readonly Real NegativeInfinity = new Real(EDecimal.NegativeInfinity);
/// Positive Infinity (+oo)
- public static readonly Real PositiveInfinity = new Real(EDecimal.PositiveInfinity);
+ [ConstantField] public static readonly Real PositiveInfinity = new Real(EDecimal.PositiveInfinity);
/// Not A Number (NaN)
- public static readonly Real NaN = new Real(EDecimal.NaN);
+ [ConstantField] public static readonly Real NaN = new Real(EDecimal.NaN);
///
/// Converts the given number to a double (not recommended in general unless you need a built-in type)
diff --git a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs
index fe0ecbee4..ded4d889b 100644
--- a/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs
+++ b/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs
@@ -18,7 +18,7 @@ partial record Entity
///
/// A node of sine
///
- public partial record Sinf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Sinf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Sinf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -31,7 +31,7 @@ public partial record Sinf(Entity Argument) : TrigonometricFunction
///
/// A node of cosine
///
- public partial record Cosf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Cosf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Cosf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -44,7 +44,7 @@ public partial record Cosf(Entity Argument) : TrigonometricFunction
///
/// A node of tangent
///
- public partial record Tanf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Tanf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Tanf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -57,7 +57,7 @@ public partial record Tanf(Entity Argument) : TrigonometricFunction
///
/// A node of cotangent
///
- public partial record Cotanf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Cotanf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Cotanf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -70,7 +70,7 @@ public partial record Cotanf(Entity Argument) : TrigonometricFunction
///
/// A node of secant
///
- public partial record Secantf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Secantf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Secantf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
@@ -83,7 +83,7 @@ public partial record Secantf(Entity Argument) : TrigonometricFunction
///
/// A node of secant
///
- public partial record Cosecantf(Entity Argument) : TrigonometricFunction
+ public sealed partial record Cosecantf(Entity Argument) : TrigonometricFunction
{
/// Reuse the cache by returning the same object if possible
private Cosecantf New(Entity argument) => ReferenceEquals(Argument, argument) ? this : new(argument);
diff --git a/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs b/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs
index 615b5513f..fe477a5b6 100644
--- a/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs
+++ b/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs
@@ -26,13 +26,13 @@ public sealed partial record Boolean(bool Value) : Statement
/// One of the Boolean's state, which also behaves as Entity
/// That is, hangable
///
- public static readonly Boolean True = new Boolean(true);
+ [ConstantField] public static readonly Boolean True = new Boolean(true);
///
/// One of the Boolean's state, which also behaves as Entity
/// That is, hangable
///
- public static readonly Boolean False = new Boolean(false);
+ [ConstantField] public static readonly Boolean False = new Boolean(false);
///
/// This conversation is 100% free, no need to manually choose between
diff --git a/AngouriMath/Core/Entity/Entity.Definition.cs b/AngouriMath/Core/Entity/Entity.Definition.cs
index 1ed6a7628..840dda119 100644
--- a/AngouriMath/Core/Entity/Entity.Definition.cs
+++ b/AngouriMath/Core/Entity/Entity.Definition.cs
@@ -81,7 +81,10 @@ namespace AngouriMath
///
public abstract partial record Entity : ILatexiseable
{
- internal static RecordFieldCache caches = new();
+ // There should be guarantee that RecordFieldCache is thread-safe
+ internal static RecordFieldCache Caches => caches ??= new();
+ [ThreadStatic] internal static RecordFieldCache? caches;
+
///
protected abstract Entity[] InitDirectChildren();
@@ -90,7 +93,7 @@ public abstract partial record Entity : ILatexiseable
/// Represents all direct children of a node
///
public IReadOnlyList DirectChildren
- => caches.GetValue(this, cache => cache.directChildren, cache => cache.directChildren = InitDirectChildren());
+ => Caches.GetValue(this, cache => cache.directChildren, cache => cache.directChildren = InitDirectChildren());
/// A depth-first enumeration is required by
///
@@ -131,7 +134,7 @@ public Entity Substitute(IReadOnlyDictionary replacement
/// meaning that it could be safely used for calculations
///
public bool IsFinite
- => caches.GetValue(this, cache => cache.isFinite, cache => cache.isFinite =
+ => Caches.GetValue(this, cache => cache.isFinite, cache => cache.isFinite =
ThisIsFinite && DirectChildren.All(x => x.IsFinite)) ?? throw new AngouriBugException($"{IsFinite} cannot be null");
///
@@ -143,7 +146,7 @@ public bool IsFinite
// TODO: improve measurement of Entity complexity, for example
// (1 / x ^ 2).Complexity() < (x ^ (-0.5)).Complexity()
public int Complexity
- => caches.GetValue(this,
+ => Caches.GetValue(this,
cache => cache.complexity,
cache => cache.complexity = 1 + DirectChildren.Sum(x => x.Complexity)) ?? throw new AngouriBugException("Complexity cannot be null");
@@ -166,7 +169,7 @@ public int Complexity
/// such as and
///
public IReadOnlyCollection VarsAndConsts
- => caches.GetValue(this, cache => cache.vars,
+ => Caches.GetValue(this, cache => cache.vars,
cache => cache.vars =
new HashSet(this is Variable v ? new[] { v } : DirectChildren.SelectMany(x => x.VarsAndConsts)));
@@ -187,7 +190,7 @@ public IReadOnlyCollection VarsAndConsts
/// shows how convenient it is to view the expression. This depends on
/// which can be changed by user.
///
- public int SimplifiedRate => caches.GetValue(this, cache => cache.simplifiedRate, cache => cache.simplifiedRate = MathS.Settings.ComplexityCriteria.Value(this)) ?? throw new AngouriBugException("Sim cannot be null");
+ public double SimplifiedRate => Caches.GetValue(this, cache => cache.simplifiedRate, cache => cache.simplifiedRate = MathS.Settings.ComplexityCriteria.Value(this)) ?? throw new AngouriBugException("Sim cannot be null");
/// Checks whether the given expression contains variable
public bool IsSymbolic => Vars.Any();
diff --git a/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs b/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs
index 992d09849..80229ba9b 100644
--- a/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs
+++ b/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs
@@ -29,7 +29,7 @@ public partial record Set : Entity
///
/// A finite set is a set whose elements can be counted and enumerated
///
- public partial record FiniteSet : Set, IReadOnlyCollection, IEquatable
+ public sealed partial record FiniteSet : Set, IReadOnlyCollection, IEquatable
{
///
/// The IEnumerable of elements of a finite set
@@ -162,7 +162,7 @@ public override int GetHashCode()
/// Checks that two FiniteSets are equal
/// If one is not FiniteSet, the method returns false
///
- public virtual bool Equals(FiniteSet other)
+ public bool Equals(FiniteSet other)
{
if (other is null)
return false;
@@ -232,7 +232,7 @@ public bool TryIsSubsetOf(FiniteSet superSet, out bool isSub)
/// stands for whether is included
/// stands for whether is included
///
- public partial record Interval(Entity Left, bool LeftClosed, Entity Right, bool RightClosed) : Set, IEquatable
+ public sealed partial record Interval(Entity Left, bool LeftClosed, Entity Right, bool RightClosed) : Set, IEquatable
{
///
/// Checks whether the interval's ends are both numerical (convenient for some evaluations)
@@ -305,7 +305,7 @@ public override bool TryContains(Entity entity, out bool contains)
/// Checks that two intervals are equal
/// If one is not interval, false is returned
///
- public virtual bool Equals(Interval other)
+ public bool Equals(Interval other)
=> other is not null && (Left == other.Left
&& Right == other.Right
&& LeftClosed == other.LeftClosed
@@ -335,7 +335,7 @@ public override int GetHashCode()
/// a condition F(x) in the following way: for each element x in
/// the Universal x belongs to A if and only if F(x).
///
- public partial record ConditionalSet(Entity Var, Entity Predicate) : Set, IEquatable
+ public sealed partial record ConditionalSet(Entity Var, Entity Predicate) : Set, IEquatable
{
///
public override Entity Replace(Func func)
@@ -376,7 +376,7 @@ internal Entity New(Entity var, Entity predicate)
/// Compares two ConditionalSets
/// If one is not CSet, false is returned
///
- public virtual bool Equals(ConditionalSet other)
+ public bool Equals(ConditionalSet other)
{
if (other is null) // invalid cast
return false;
@@ -385,7 +385,7 @@ public virtual bool Equals(ConditionalSet other)
}
- private readonly static Variable universalVoidConstant = Variable.CreateVariableUnchecked("%");
+ [ConstantField] private readonly static Variable universalVoidConstant = Variable.CreateVariableUnchecked("%");
///
public override int GetHashCode()
// TODO: might not always work, requires testing
@@ -407,7 +407,7 @@ public override Entity Replace(Func func)
// Since there's a very small number of domains, it's wiser to
// cache them all
private static Dictionary InnerStorage => innerStorage ??= new();
- [ThreadStatic] private static Dictionary? innerStorage = null;
+ [ThreadStatic] private static Dictionary? innerStorage;
///
/// Creates an instance of special set from a domain
@@ -565,13 +565,13 @@ public override bool TryContains(Entity entity, out bool contains)
protected override Entity[] InitDirectChildren() => new[] { Left, Right };
///
- public override bool IsSetFinite => caches.GetValue(this,
+ public override bool IsSetFinite => Caches.GetValue(this,
cache => cache.isSetFinite, cache => cache.isSetFinite =
Left is FiniteSet finite1 && Right is FiniteSet finite2
&& finite1.IsSetFinite && finite2.IsSetFinite) ?? throw new AngouriBugException("isSetFinite cannot be null");
///
- public override bool IsSetEmpty => caches.GetValue(this,
+ public override bool IsSetEmpty => Caches.GetValue(this,
cache => cache.isSetEmpty, cache => cache.isSetEmpty =
Left is FiniteSet finite1 && Right is FiniteSet finite2
&& finite1.IsSetEmpty && finite2.IsSetEmpty) ?? throw new AngouriBugException("isSetEmpty cannot be null");
@@ -612,12 +612,12 @@ public override bool TryContains(Entity entity, out bool contains)
protected override Entity[] InitDirectChildren() => new[] { Left, Right };
///
- public override bool IsSetFinite => caches.GetValue(this,
+ public override bool IsSetFinite => Caches.GetValue(this,
cache => cache.isSetFinite, cache => cache.isSetFinite = Left is FiniteSet finite1 && Right is FiniteSet finite2
&& (finite1.IsSetFinite || finite2.IsSetFinite)) ?? throw new AngouriBugException("isSetFinite cannot be null");
///
- public override bool IsSetEmpty => caches.GetValue(this,
+ public override bool IsSetEmpty => Caches.GetValue(this,
cache => cache.isSetEmpty, cache => cache.isSetEmpty =
Left is FiniteSet finite1 && Right is FiniteSet finite2
&& (finite1.IsSetEmpty || finite2.IsSetEmpty)) ?? throw new AngouriBugException("isSetEmpty cannot be null");
@@ -658,12 +658,12 @@ public override bool TryContains(Entity entity, out bool contains)
protected override Entity[] InitDirectChildren() => new[] { Left, Right };
///
- public override bool IsSetFinite => caches.GetValue(this,
+ public override bool IsSetFinite => Caches.GetValue(this,
cache => cache.isSetFinite, cache => cache.isSetFinite =
Left is FiniteSet finite1 && Right is FiniteSet && finite1.IsSetFinite) ?? throw new AngouriBugException("isSetFinite cannot be null");
///
- public override bool IsSetEmpty => caches.GetValue(this,
+ public override bool IsSetEmpty => Caches.GetValue(this,
cache => cache.isSetEmpty, cache => cache.isSetEmpty =
Left is FiniteSet finite1 && Right is FiniteSet finite2
&& (finite1.IsSetEmpty || finite1 == finite2)) ?? throw new AngouriBugException("isSetEmpty cannot be null");
diff --git a/AngouriMath/Core/Entity/Omni/Entity.Set.cs b/AngouriMath/Core/Entity/Omni/Entity.Set.cs
index cf8d71fef..bc596ac48 100644
--- a/AngouriMath/Core/Entity/Omni/Entity.Set.cs
+++ b/AngouriMath/Core/Entity/Omni/Entity.Set.cs
@@ -38,13 +38,13 @@ public abstract partial record Set
/// Thrown when
public bool Contains(Entity entity)
=> TryContains(entity, out var res) ? res : throw new ElementInSetAmbiguousException("Cannot determine whether the element is in the set");
-
+
///
/// Returns an empty set
/// You can use it to compare sets to it
/// or to avoid allocations
///
- public readonly static FiniteSet Empty = new FiniteSet();
+ [ConstantField] public readonly static FiniteSet Empty = new FiniteSet();
///
/// Checks that a set is finite
diff --git a/AngouriMath/Core/Entity/Omni/Entity.Tensor.cs b/AngouriMath/Core/Entity/Omni/Entity.Tensor.cs
index 859d664ed..40fdd5c80 100644
--- a/AngouriMath/Core/Entity/Omni/Entity.Tensor.cs
+++ b/AngouriMath/Core/Entity/Omni/Entity.Tensor.cs
@@ -21,7 +21,7 @@ partial record Entity
/// Basic tensor implementation:
#pragma warning disable CS1591 // TODO: it's only for records' parameters! Remove it once you can document records parameters
- public partial record Tensor(GenTensor InnerTensor) : Entity
+ public sealed partial record Tensor(GenTensor InnerTensor) : Entity
#pragma warning restore CS1591 // TODO: it's only for records' parameters! Remove it once you can document records parameters
{
/// Reuse the cache by returning the same object if possible
@@ -124,7 +124,7 @@ public Tensor(Func operation, params int[] dims) : this(GenTensor
public void Transpose()
{
if (IsMatrix) InnerTensor.TransposeMatrix();
- else throw new Core.Exceptions.MathSException("Specify axes numbers for non-matrices");
+ else throw new Core.Exceptions.InvalidMatrixOperationException("Specify axes numbers for non-matrices");
}
// We do not need to use Gaussian elimination here
@@ -132,7 +132,7 @@ public void Transpose()
///
/// Finds the symbolical determinant via Laplace's method
///
- public Entity Determinant() => InnerTensor.DeterminantLaplace();
+ public Entity Determinant() => InnerTensor.DeterminantLaplace().InnerSimplified;
/// Inverts all matrices in a tensor
public Tensor Inverse()
diff --git a/AngouriMath/Core/Entity/Omni/Entity.Variable.cs b/AngouriMath/Core/Entity/Omni/Entity.Variable.cs
index 8f7d644b1..1f5b4040f 100644
--- a/AngouriMath/Core/Entity/Omni/Entity.Variable.cs
+++ b/AngouriMath/Core/Entity/Omni/Entity.Variable.cs
@@ -24,7 +24,7 @@ partial record Entity
/// Construct a with an implicit conversion from .
///
///
- public partial record Variable : Entity
+ public sealed partial record Variable : Entity
{
///
/// Deconstructs Variable as follows
@@ -46,9 +46,9 @@ public void Deconstruct(out string name)
///
protected override Entity[] InitDirectChildren() => Array.Empty();
- internal static readonly Variable pi = new Variable(nameof(pi));
- internal static readonly Variable e = new Variable(nameof(e));
- internal static readonly IReadOnlyDictionary ConstantList =
+ [ConstantField] internal static readonly Variable pi = new Variable(nameof(pi));
+ [ConstantField] internal static readonly Variable e = new Variable(nameof(e));
+ [ConstantField] internal static readonly IReadOnlyDictionary ConstantList =
new Dictionary
{
{ pi, MathS.DecimalConst.pi },
diff --git a/AngouriMath/Core/EquationSystem.cs b/AngouriMath/Core/EquationSystem.cs
index be4137946..79f95d17f 100644
--- a/AngouriMath/Core/EquationSystem.cs
+++ b/AngouriMath/Core/EquationSystem.cs
@@ -8,6 +8,7 @@
* 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.Generic;
+using System.Linq;
using System.Text;
using AngouriMath.Functions.Algebra;
using static AngouriMath.Entity;
@@ -64,5 +65,8 @@ public string Latexise()
sb.Append(@"\end{cases}");
return sb.ToString();
}
+
+ ///
+ public override string ToString() => string.Join("\n", equations.Select(c => c.Stringize() + " = 0"));
}
}
\ No newline at end of file
diff --git a/AngouriMath/Core/Exceptions/InvalidExpressionException.cs b/AngouriMath/Core/Exceptions/InvalidExpressionException.cs
deleted file mode 100644
index 4f2e3a328..000000000
--- a/AngouriMath/Core/Exceptions/InvalidExpressionException.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace AngouriMath.Core.Exceptions
-{
- class InvalidExpressionException
- {
- }
-}
diff --git a/AngouriMath/Core/Exceptions/SysExceptions.cs b/AngouriMath/Core/Exceptions/SysExceptions.cs
index 7ffa7c1e4..09ee752b7 100644
--- a/AngouriMath/Core/Exceptions/SysExceptions.cs
+++ b/AngouriMath/Core/Exceptions/SysExceptions.cs
@@ -37,4 +37,14 @@ internal static Exception Raised(string feature, string plannedVersion)
return new FutureReleaseException($"Feature {feature} will be completed by {plannedVersion}. You are on {currVersion}");
}
}
+
+ ///
+ /// In case if AM or other parts do not support something,
+ /// for example, it may occur if either AM or SymPy does not
+ /// support some specific feature
+ ///
+ public sealed class NotSufficientlySupportedException : Exception
+ {
+ internal NotSufficientlySupportedException(string msg) : base(msg) { }
+ }
}
\ No newline at end of file
diff --git a/AngouriMath/Core/Exceptions/UserInputInvalidException.cs b/AngouriMath/Core/Exceptions/UserInputInvalidException.cs
index 67888b5f6..76b8df183 100644
--- a/AngouriMath/Core/Exceptions/UserInputInvalidException.cs
+++ b/AngouriMath/Core/Exceptions/UserInputInvalidException.cs
@@ -3,10 +3,16 @@
namespace AngouriMath.Core.Exceptions
{
/// If one is thrown, the user's input is invalid
- public class MathSException : ArgumentException { internal MathSException(string message) : base(message) { } }
+ public abstract class MathSException : ArgumentException { internal MathSException(string message) : base(message) { } }
+
+ /// Thrown inside operations related to limits
+ public sealed class LimitOperationNotSupportedException : MathSException { internal LimitOperationNotSupportedException(string message) : base(message) { } }
+
+ /// Quite general, is thrown when too many or too few of whatever is provided
+ public sealed class WrongNumberOfArgumentsException : MathSException { internal WrongNumberOfArgumentsException(string message) : base(message) { } }
/// Thrown when an invalid node or combination of nodes in the expression tree is encountered
- public class TreeException : MathSException { internal TreeException(string message) : base(message) { } }
+ public abstract class TreeException : MathSException { internal TreeException(string message) : base(message) { } }
/// Thrown when trying to compile and a node cannot be compiled
public sealed class UncompilableNodeException : TreeException { internal UncompilableNodeException(string message) : base(message) { } }
@@ -18,5 +24,11 @@ public sealed class CannotEvalException : MathSException { internal CannotEvalEx
public sealed class ElementInSetAmbiguousException : MathSException { internal ElementInSetAmbiguousException(string msg) : base(msg) { } }
/// Thrown if instead of a statement another expression is put into Solve
- public sealed class SolveRequiresStatement : MathSException { internal SolveRequiresStatement() : base("There should be statement to be true (e. g. equality, inequality, or some other predicate)") { } }
+ public sealed class SolveRequiresStatementException : MathSException { internal SolveRequiresStatementException() : base("There should be statement to be true (e. g. equality, inequality, or some other predicate)") { } }
+
+ /// Thrown in matrix operations
+ public sealed class InvalidMatrixOperationException : MathSException { internal InvalidMatrixOperationException(string msg) : base(msg) { } }
+
+ ///
+ public sealed class InvalidNumericSystemException : MathSException { internal InvalidNumericSystemException(string msg) : base(msg) { } }
}
diff --git a/AngouriMath/Core/Parser.cs b/AngouriMath/Core/Parser.cs
index 2ddc23d35..2fc21420c 100644
--- a/AngouriMath/Core/Parser.cs
+++ b/AngouriMath/Core/Parser.cs
@@ -28,7 +28,7 @@ namespace AngouriMath.Core
static class Parser
{
// Antlr parser spams errors into TextWriter provided, we inherit from it to handle lexer/parser errors as ParseExceptions
- class AngouriMathTextWriter : TextWriter
+ internal sealed class AngouriMathTextWriter : TextWriter
{
public override Encoding Encoding => Encoding.UTF8;
public override void WriteLine(string s) => throw new UnhandledParseException(s);
diff --git a/AngouriMath/Core/RecordFieldCache.cs b/AngouriMath/Core/RecordFieldCache.cs
index fb21ce694..f61485f21 100644
--- a/AngouriMath/Core/RecordFieldCache.cs
+++ b/AngouriMath/Core/RecordFieldCache.cs
@@ -23,7 +23,7 @@ internal sealed class EntityCache
internal Entity[]? directChildren;
internal int? complexity;
internal bool? isFinite;
- internal int? simplifiedRate;
+ internal double? simplifiedRate;
internal Entity? innerSimplified;
internal Entity? innerEvaled;
internal HashSet? vars;
diff --git a/AngouriMath/Functions/BaseConversion.cs b/AngouriMath/Functions/BaseConversion.cs
index 9aa9599da..7aa7bf3a4 100644
--- a/AngouriMath/Functions/BaseConversion.cs
+++ b/AngouriMath/Functions/BaseConversion.cs
@@ -12,6 +12,7 @@
using System.Collections.Generic;
using PeterO.Numbers;
using System.Linq;
+using AngouriMath.Core;
namespace AngouriMath.Functions
{
@@ -19,8 +20,8 @@ namespace AngouriMath.Functions
internal static class BaseConversion
{
// TODO: add more digits
- internal static readonly string ALPHABET_TOCHAR = "0123456789ABCDEF";
- internal static readonly Dictionary ALPHABET_FROMCHAR =
+ [ConstantField] internal static readonly string ALPHABET_TOCHAR = "0123456789ABCDEF";
+ [ConstantField] internal static readonly Dictionary ALPHABET_FROMCHAR =
ALPHABET_TOCHAR.ToDictionary(c => c, ALPHABET_TOCHAR.IndexOf);
/// Transforms an integer to the corresponding base (in string)
@@ -78,7 +79,7 @@ internal static string FloatToBaseN(EDecimal num /*should be < 1*/, int N)
internal static string ToBaseN(EDecimal num, int N)
{
if (N > ALPHABET_TOCHAR.Length)
- throw new MathSException("N should be <= than " + ALPHABET_TOCHAR.Length);
+ throw new InvalidNumericSystemException("N should be <= than " + ALPHABET_TOCHAR.Length);
string sign = num.IsNegative ? "-" : "";
num = num.Abs();
var (intPart, floatPart) = num.SplitDecimal();
diff --git a/AngouriMath/Functions/Continuous/Limits/Limit.Definition.cs b/AngouriMath/Functions/Continuous/Limits/Limit.Definition.cs
index 57f70a5b8..f7e2c271a 100644
--- a/AngouriMath/Functions/Continuous/Limits/Limit.Definition.cs
+++ b/AngouriMath/Functions/Continuous/Limits/Limit.Definition.cs
@@ -211,7 +211,7 @@ private static bool IsInfiniteNode(Entity expr)
// compute limit for x -> +oo
Real { IsFinite: false, IsNegative: false } => SimplifyAndComputeLimitToInfinity(expr, x),
Complex { IsFinite: false } =>
- throw new Core.Exceptions.MathSException($"complex infinities are not supported in limits: lim({x} -> {dist}) {expr}"),
+ throw new Core.Exceptions.LimitOperationNotSupportedException($"Complex infinities are not supported in limits: lim({x} -> {dist}) {expr}"),
_ => SimplifyAndComputeLimitToInfinity(side switch
{
// lim(x -> 3-) x <=> lim(x -> 0+) 3 - x <=> lim(x -> +oo) 3 - 1 / x
diff --git a/AngouriMath/Functions/Continuous/Limits/LimitSolvers.cs b/AngouriMath/Functions/Continuous/Limits/LimitSolvers.cs
index 9104a2303..273c083e2 100644
--- a/AngouriMath/Functions/Continuous/Limits/LimitSolvers.cs
+++ b/AngouriMath/Functions/Continuous/Limits/LimitSolvers.cs
@@ -7,6 +7,7 @@
*
* 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 AngouriMath.Core;
using PeterO.Numbers;
using System.Collections.Generic;
using System.Linq;
@@ -15,7 +16,7 @@ namespace AngouriMath.Functions.Algebra
{
using static Entity;
using static Entity.Number;
- class LimitSolvers
+ internal static class LimitSolvers
{
internal static Dictionary? ParseAsPolynomial(Entity expr, Variable x)
{
@@ -40,7 +41,7 @@ class LimitSolvers
}
return filteredDictionary;
}
- private static readonly Real Infinity = Real.PositiveInfinity;
+ [ConstantField] private static readonly Real Infinity = Real.PositiveInfinity;
internal static Entity? SolveBySubstitution(Entity expr, Variable x)
{
var res = expr.Substitute(x, Infinity);
diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs
index eecd0d735..305066545 100644
--- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs
+++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver.cs
@@ -59,7 +59,7 @@ internal static Set Solve(Entity equation, Variable x)
{
var equations = new List(inputEquations.Select(equation => equation.InnerSimplified));
if (equations.Count != vars.Length)
- throw new MathSException("Amount of equations must be equal to that of vars");
+ throw new WrongNumberOfArgumentsException("Number of equations must be equal to that of vars");
int initVarCount = vars.Length;
var res = InSolveSystem(equations, vars);
diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs
index ce61663b4..4e9d693c5 100644
--- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs
+++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs
@@ -8,15 +8,15 @@
namespace AngouriMath.Functions.Algebra.AnalyticalSolving
{
- class ExponentialSolver
+ internal static class ExponentialSolver
{
- internal static Entity.Set? SolveLinear(Entity expr, Entity.Variable x)
+ internal static Set? SolveLinear(Entity expr, Entity.Variable x)
{
- var replacement = Entity.Variable.CreateTemp(expr.Vars);
+ var replacement = Variable.CreateTemp(expr.Vars);
Func preparator = e => e switch
{
- Entity.Powf(var @base, var arg) when
+ Powf(var @base, var arg) when
TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) =>
MathS.Pow(@base, b) * MathS.Pow(MathS.Pow(MathS.e, x), MathS.Ln(@base) * a),
@@ -25,7 +25,7 @@ class ExponentialSolver
Func replacer = e => e switch
{
- Entity.Powf(var @base, var arg)
+ Powf(var @base, var arg)
when @base == MathS.e && arg == x
=> replacement,
@@ -39,7 +39,7 @@ class ExponentialSolver
expr = expr.InnerSimplified;
if (AnalyticalEquationSolver.Solve(expr, replacement) is FiniteSet els && els.Any())
- return (Entity.Set)els.Select(sol => MathS.Pow(MathS.e, x).Invert(sol, x).ToSet()).Unite().InnerSimplified;
+ return (Set)els.Select(sol => MathS.Pow(MathS.e, x).Invert(sol, x).ToSet()).Unite().InnerSimplified;
else
return null;
}
@@ -52,7 +52,7 @@ internal static Entity GetConstantOutOfLogarithm(Entity expr)
_ => expr
};
- internal static Entity.Set? SolveMultiplicative(Entity expr, Entity.Variable x)
+ internal static Set? SolveMultiplicative(Entity expr, Variable x)
{
Entity? substitution = null;
var innerPowerList = new List();
@@ -61,7 +61,7 @@ Entity ApplyPowerTransform(Entity @base, Entity arg)
{
arg = arg.Replace(GetConstantOutOfLogarithm);
var mults = Entity.Mulf.LinearChildren(arg);
- if (mults.Count() == 0) return MathS.Pow(@base, arg);
+ if (!mults.Any()) return MathS.Pow(@base, arg);
Entity innerPower = 1;
Entity outerPower = 1;
@@ -80,7 +80,7 @@ Entity ApplyPowerTransform(Entity @base, Entity arg)
Func powerTransform = e => e switch
{
- Entity.Powf(var @base, var arg)
+ Powf(var @base, var arg)
when @base == x && !arg.ContainsNode(x) =>
ApplyPowerTransform(@base, arg),
@@ -90,7 +90,7 @@ Entity ApplyPowerTransform(Entity @base, Entity arg)
expr = expr.Replace(powerTransform);
if (substitution is null) return null;
- var replacement = Entity.Variable.CreateTemp(expr.Vars);
+ var replacement = Variable.CreateTemp(expr.Vars);
if(innerPowerList.Count == 0)
(innerPowerList, outerPowerList) = (outerPowerList, innerPowerList);
diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs
index 9fb438d66..4eaa465c3 100644
--- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs
+++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs
@@ -152,8 +152,8 @@ private protected override IEnumerable InvertNode(Entity value, Entity x
partial record Arcsinf
{
- private static readonly Complex From = Complex.Create(-MathS.DecimalConst.pi / 2, Real.NegativeInfinity.EDecimal);
- private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi / 2, Real.PositiveInfinity.EDecimal);
+ [ConstantField] private static readonly Complex From = Complex.Create(-MathS.DecimalConst.pi / 2, Real.NegativeInfinity.EDecimal);
+ [ConstantField] private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi / 2, Real.PositiveInfinity.EDecimal);
// arcsin(x) = value => x = sin(value)
private protected override IEnumerable InvertNode(Entity value, Entity x) =>
EntityInBounds(value, From, To) ? Argument.Invert(MathS.Sin(value), x) : Enumerable.Empty();
@@ -161,17 +161,17 @@ private protected override IEnumerable InvertNode(Entity value, Entity x
partial record Arccosf
{
- private static readonly Complex From = Complex.Create(0, Real.NegativeInfinity.EDecimal);
- private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi, Real.PositiveInfinity.EDecimal);
+ [ConstantField] private static readonly Complex From = Complex.Create(0, Real.NegativeInfinity.EDecimal);
+ [ConstantField] private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi, Real.PositiveInfinity.EDecimal);
// arccos(x) = value => x = cos(value)
private protected override IEnumerable InvertNode(Entity value, Entity x) =>
EntityInBounds(value, From, To) ? Argument.Invert(MathS.Cos(value), x) : Enumerable.Empty();
}
-
+
partial record Arctanf
{
- private static readonly Complex From = Complex.Create(-MathS.DecimalConst.pi / 2, Real.NegativeInfinity.EDecimal);
- private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi / 2, Real.PositiveInfinity.EDecimal);
+ [ConstantField] private static readonly Complex From = Complex.Create(-MathS.DecimalConst.pi / 2, Real.NegativeInfinity.EDecimal);
+ [ConstantField] private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi / 2, Real.PositiveInfinity.EDecimal);
// arctan(x) = value => x = tan(value)
private protected override IEnumerable InvertNode(Entity value, Entity x) =>
EntityInBounds(value, From, To) ? Argument.Invert(MathS.Tan(value), x) : Enumerable.Empty();
@@ -180,8 +180,8 @@ private protected override IEnumerable InvertNode(Entity value, Entity x
partial record Arccotanf
{
// TODO: Range should exclude Re(z) = 0
- private static readonly Complex From = Complex.Create(-MathS.DecimalConst.pi / 2, Real.NegativeInfinity.EDecimal);
- private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi / 2, Real.PositiveInfinity.EDecimal);
+ [ConstantField] private static readonly Complex From = Complex.Create(-MathS.DecimalConst.pi / 2, Real.NegativeInfinity.EDecimal);
+ [ConstantField] private static readonly Complex To = Complex.Create(MathS.DecimalConst.pi / 2, Real.PositiveInfinity.EDecimal);
// arccotan(x) = value => x = cotan(value)
private protected override IEnumerable InvertNode(Entity value, Entity x) =>
EntityInBounds(value, From, To) ? Argument.Invert(MathS.Cotan(value), x) : Enumerable.Empty();
diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/PolynomialSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/PolynomialSolver.cs
index 164141129..e4f4483f3 100644
--- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/PolynomialSolver.cs
+++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/PolynomialSolver.cs
@@ -9,6 +9,7 @@
*/
using System.Collections.Generic;
using System.Linq;
+using AngouriMath.Core;
using PeterO.Numbers;
using static AngouriMath.Entity;
using static AngouriMath.Entity.Number;
@@ -25,7 +26,7 @@ internal interface IPrimitive
T Value { get; }
bool AllowFloat { get; }
}
- internal class PrimitiveDecimal : IPrimitive
+ internal sealed class PrimitiveDecimal : IPrimitive
{
public void Add(Real a) => Value += a.EDecimal;
public void AddMp(EDecimal a, Real b) => Value += a * b.EDecimal;
@@ -33,7 +34,7 @@ internal class PrimitiveDecimal : IPrimitive
public EDecimal Value { get; private set; } = 0;
public bool AllowFloat => true;
}
- internal class PrimitiveInteger : IPrimitive
+ internal sealed class PrimitiveInteger : IPrimitive
{
public void Add(Real a) => Value += a.EDecimal.ToEInteger();
public void AddMp(EInteger a, Real b) => Value += (a * b.EDecimal).ToEInteger();
@@ -103,7 +104,7 @@ internal static IEnumerable SolveCubic(Entity a, Entity b, Entity c, Ent
C.Evaled == 0 && D0.Evaled == 0 ? -(b + uk * C) / 3 / a : -(b + uk * C + D0 / C / uk) / 3 / a);
}
- static readonly int[] sqrtsOf1 = { -1, 1 };
+ [ConstantField] private static readonly int[] sqrtsOf1 = { -1, 1 };
/// Solves ax^4 + bx^3 + cx^2 + dx + e
/// Coefficient of x^4
/// Coefficient of x^3
@@ -288,7 +289,7 @@ internal static (Entity FreeMono, TPrimitive Power)? ParseMonomial
return ChooseGood();
}
if (expr.Vars.Single() != v)
- throw new Core.Exceptions.MathSException($"{nameof(expr)} should only contain {nameof(Entity.Variable)} {nameof(v)}");
+ throw new Core.Exceptions.WrongNumberOfArgumentsException($"{nameof(expr)} should only contain {nameof(Entity.Variable)} {nameof(v)}");
return MathS.Settings.FloatToRationalIterCount.As(0, () =>
{
var res = new HashSet();
diff --git a/AngouriMath/Functions/Continuous/Solvers/Solvers.Definition.cs b/AngouriMath/Functions/Continuous/Solvers/Solvers.Definition.cs
index 14f761e9b..e7ca950a6 100644
--- a/AngouriMath/Functions/Continuous/Solvers/Solvers.Definition.cs
+++ b/AngouriMath/Functions/Continuous/Solvers/Solvers.Definition.cs
@@ -32,7 +32,7 @@ public Set Solve(Variable var)
}
if (this == var)
return new FiniteSet(Boolean.True);
- throw new SolveRequiresStatement();
+ throw new SolveRequiresStatementException();
}
}
}
diff --git a/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs b/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs
index c14903b67..77bc368cb 100644
--- a/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs
+++ b/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs
@@ -28,7 +28,7 @@ public abstract partial record Entity
/// symbolic algebra systems it is called "Automatic simplification"
///
public Entity InnerSimplified
- => caches.GetValue(this, cache => cache.innerSimplified, cache => cache.innerSimplified = InnerSimplifyWithCheck());
+ => Caches.GetValue(this, cache => cache.innerSimplified, cache => cache.innerSimplified = InnerSimplifyWithCheck());
///
/// Make sure you call this function inside of
@@ -115,7 +115,7 @@ public Entity Factorize(int level = 2) => level <= 1
/// (cached value)
///
public Entity Evaled
- => caches.GetValue(this, cache => cache.innerEvaled, cache => cache.innerEvaled = InnerEvalWithCheck());
+ => Caches.GetValue(this, cache => cache.innerEvaled, cache => cache.innerEvaled = InnerEvalWithCheck());
///
/// Whether the expression can be collapsed to a tensor
diff --git a/AngouriMath/Functions/NumbersExtensions.cs b/AngouriMath/Functions/NumbersExtensions.cs
index 32d81de02..3579e8d53 100644
--- a/AngouriMath/Functions/NumbersExtensions.cs
+++ b/AngouriMath/Functions/NumbersExtensions.cs
@@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
+using AngouriMath.Core;
using PeterO.Numbers;
//[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
@@ -30,16 +31,17 @@ public sealed class ConstantCache
{
public static ConstantCache Lookup(EContext context)
{
- if (!constants.TryGetValue(context, out var cache))
- lock (constants)
- if (!constants.TryGetValue(context, out cache))
+ if (!Constants.TryGetValue(context, out var cache))
+ lock (Constants)
+ if (!Constants.TryGetValue(context, out cache))
{
cache = new ConstantCache(context);
- constants.Add(context, cache);
+ Constants.Add(context, cache);
}
return cache;
}
- static readonly Dictionary constants = new Dictionary();
+ private static Dictionary Constants => constants ??= new();
+ [ThreadStatic] private static Dictionary? constants;
ConstantCache(EContext context)
{
Half = EDecimal.One.Divide(2, context);
@@ -468,9 +470,9 @@ static IEnumerable GenerateFactorials()
while (true)
yield return result *= ++i;
}
- static readonly IEnumerator factorialCacheGenerator = GenerateFactorials().GetEnumerator();
- static readonly List factorialCache = new List();
- static readonly Dictionary spougeFactorialConstantsCache = new Dictionary();
+ [ConstantField] static readonly IEnumerator factorialCacheGenerator = GenerateFactorials().GetEnumerator();
+ [ConstantField] static readonly List factorialCache = new List();
+ [ConstantField] static readonly Dictionary spougeFactorialConstantsCache = new Dictionary();
/**
diff --git a/AngouriMath/Functions/Output/Latex.Classes.cs b/AngouriMath/Functions/Output/Latex.Classes.cs
index 0aac494ed..6144abc2f 100644
--- a/AngouriMath/Functions/Output/Latex.Classes.cs
+++ b/AngouriMath/Functions/Output/Latex.Classes.cs
@@ -77,7 +77,7 @@ public partial record Variable
/// Yet to be extended
/// Case does matter, not all letters have both displays in LaTeX
///
- private static readonly HashSet LatexisableConstants = new HashSet
+ [ConstantField] private static readonly HashSet LatexisableConstants = new HashSet
{
"alpha", "beta", "gamma", "delta", "epsilon", "varepsilon", "zeta", "eta", "theta", "vartheta",
"iota", "kappa", "varkappa", "lambda", "mu", "nu", "xi", "omicron", "pi", "varpi", "rho",
diff --git a/AngouriMath/Functions/Output/ToSympy.Classes.cs b/AngouriMath/Functions/Output/ToSympy.Classes.cs
index 2573bd5e0..d17d6989b 100644
--- a/AngouriMath/Functions/Output/ToSympy.Classes.cs
+++ b/AngouriMath/Functions/Output/ToSympy.Classes.cs
@@ -322,7 +322,7 @@ internal override string ToSymPy()
}
internal override string ToSymPy()
- => throw new MathSException($"There is no {this} in either SymPy or AM's {nameof(ToSymPy)}");
+ => throw new NotSufficientlySupportedException($"There is no {this} in either SymPy or AM's {nameof(ToSymPy)}");
}
partial record Unionf
diff --git a/AngouriMath/Functions/Simplification/Patterns/Patterns.Power.cs b/AngouriMath/Functions/Simplification/Patterns/Patterns.Power.cs
index 6ee928a6c..d70e9028e 100644
--- a/AngouriMath/Functions/Simplification/Patterns/Patterns.Power.cs
+++ b/AngouriMath/Functions/Simplification/Patterns/Patterns.Power.cs
@@ -92,6 +92,9 @@ expr is Sumf(var any1, Mulf(Real { IsNegative: true } const1, var any2))
Logf(var any1, Powf(var any2, var any3)) => any3 * MathS.Log(any1, any2),
+ Sumf(Logf(var any3, var any1), Logf(var any3a, var any2)) when any3 == any3a => any3.Log(any1 * any2),
+ Minusf(Logf(var any3, var any1), Logf(var any3a, var any2)) when any3 == any3a => any3.Log(any1 / any2),
+
_ => x
};
}
diff --git a/AngouriMath/Functions/Simplification/Patterns/Patterns.Sets.cs b/AngouriMath/Functions/Simplification/Patterns/Patterns.Sets.cs
index c80c9197e..9d72bc4f1 100644
--- a/AngouriMath/Functions/Simplification/Patterns/Patterns.Sets.cs
+++ b/AngouriMath/Functions/Simplification/Patterns/Patterns.Sets.cs
@@ -18,7 +18,7 @@ namespace AngouriMath.Functions
{
internal static partial class Patterns
{
- private static readonly FiniteSet FullBooleanSet = new FiniteSet(True, False);
+ [ConstantField] private static readonly FiniteSet FullBooleanSet = new FiniteSet(True, False);
internal static Entity SetOperatorRules(Entity x) => x switch
{
diff --git a/AngouriMath/Functions/Simplification/Simplificator.cs b/AngouriMath/Functions/Simplification/Simplificator.cs
index 8ec3f2aff..239c0f9de 100644
--- a/AngouriMath/Functions/Simplification/Simplificator.cs
+++ b/AngouriMath/Functions/Simplification/Simplificator.cs
@@ -46,15 +46,14 @@ internal static IEnumerable Alternate(Entity src, int level)
return new[] { stage1 };
// List of criteria for expr's complexity
- var history = new SortedDictionary>();
+ var history = new SortedDictionary>();
void AddHistory(Entity expr)
{
void __IterAddHistory(Entity expr)
{
- static int CountExpressionComplexity(Entity expr) => MathS.Settings.ComplexityCriteria.Value(expr);
var refexpr = expr.Replace(Patterns.SortRules(TreeAnalyzer.SortLevel.HIGH_LEVEL)).InnerSimplified;
- var compl1 = CountExpressionComplexity(refexpr);
- var compl2 = CountExpressionComplexity(expr);
+ var compl1 = refexpr.SimplifiedRate;
+ var compl2 = expr.SimplifiedRate;
var n = compl1 > compl2 ? expr : refexpr;
var ncompl = Math.Min(compl2, compl1);
if (history.TryGetValue(ncompl, out var ncomplList))
@@ -109,7 +108,7 @@ void __IterAddHistory(Entity expr)
AddHistory(res = res.Replace(Patterns.ExpandFactorialDivisions).InnerSimplified);
AddHistory(res = res.Replace(Patterns.FactorizeFactorialMultiplications).InnerSimplified);
}
- if (res.Nodes.Any(child => child is Powf))
+ if (res.Nodes.Any(child => child is Powf or Logf))
AddHistory(res = res.Replace(Patterns.PowerRules).InnerSimplified);
if (res.Nodes.Any(child => child is Set))
diff --git a/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs b/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs
index bd1c9a20b..d10405b7c 100644
--- a/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs
+++ b/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs
@@ -8,6 +8,7 @@
* 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.Generic;
+using AngouriMath.Core;
using PeterO.Numbers;
namespace AngouriMath.Functions
@@ -119,15 +120,14 @@ internal static bool PullTan(Complex arg,
private static Entity Sqrt(Entity a) => MathS.Pow(a, f1_2);
private static Entity Cbrt(Entity a) => MathS.Pow(a, f1_3);
- private static readonly Entity f1_2 = Rational.Create(1, 2);
- private static readonly Entity f1_3 = Rational.Create(1, 3);
- private static readonly Entity f1_4 = Rational.Create(1, 4);
- private static readonly Entity f1_5 = Rational.Create(1, 5);
- private static readonly Entity f1_6 = Rational.Create(1, 6);
- private static readonly Entity f1_8 = Rational.Create(1, 8);
- private static readonly Entity f1_16 = Rational.Create(1, 16);
- private static readonly Entity f1_24 = Rational.Create(1, 24);
- private static readonly Entity i = MathS.i;
+ [ConstantField] private static readonly Entity f1_2 = Rational.Create(1, 2);
+ [ConstantField] private static readonly Entity f1_3 = Rational.Create(1, 3);
+ [ConstantField] private static readonly Entity f1_4 = Rational.Create(1, 4);
+ [ConstantField] private static readonly Entity f1_5 = Rational.Create(1, 5);
+ [ConstantField] private static readonly Entity f1_6 = Rational.Create(1, 6);
+ [ConstantField] private static readonly Entity f1_8 = Rational.Create(1, 8);
+ [ConstantField] private static readonly Entity f1_16 = Rational.Create(1, 16);
+ [ConstantField] private static readonly Entity i = MathS.i;
private static EDecimal TwoPiOver(int a)
=> Number.CtxDivide(Number.CtxMultiply(2, MathS.DecimalConst.pi), a);
@@ -136,7 +136,7 @@ private static EDecimal TwoPiOver(int a)
/// Although some formulas have been changed because they are wrong on Wikipedia
///
// TODO: Some values here (e.g. sin(2pi/7)) are not present on Wikipedia. Needs additional reference sources.
- private static readonly TrigTable TableSin = new()
+ [ConstantField] private static readonly TrigTable TableSin = new()
{
(TwoPiOver(1), 0),
(TwoPiOver(2), 0),
@@ -165,7 +165,7 @@ private static EDecimal TwoPiOver(int a)
(TwoPiOver(24), f1_4 * (Sqrt(6) - Sqrt(2)))
};
- private static readonly TrigTable TableCos = new()
+ [ConstantField] private static readonly TrigTable TableCos = new()
{
(TwoPiOver(1), 1),
(TwoPiOver(2), -1),
@@ -191,7 +191,7 @@ private static EDecimal TwoPiOver(int a)
(TwoPiOver(24), f1_4 * (Sqrt(6) + Sqrt(2)))
};
- private static readonly TrigTable TableTan = new()
+ [ConstantField] private static readonly TrigTable TableTan = new()
{
(TwoPiOver(1), 0),
(TwoPiOver(2), 0),
diff --git a/AngouriMath/Functions/TreeAnalyzer/Sort.Definition.cs b/AngouriMath/Functions/TreeAnalyzer/Sort.Definition.cs
index de61bede6..dc66c3281 100644
--- a/AngouriMath/Functions/TreeAnalyzer/Sort.Definition.cs
+++ b/AngouriMath/Functions/TreeAnalyzer/Sort.Definition.cs
@@ -43,7 +43,7 @@ internal static Entity MultiHangBinary(IReadOnlyList children, Func
length switch
{
- 0 => throw new TreeException("At least 1 child required"),
+ 0 => throw new AngouriBugException("At least 1 child required"),
1 => children[start],
2 => op(children[start], children[start + 1]),
_ => op(MultiHangBinary(start, length / 2),
diff --git a/NuGet.Config b/NuGet.Config
new file mode 100644
index 000000000..3f0e00340
--- /dev/null
+++ b/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs
index c4cebb640..67bf89c7f 100644
--- a/Samples/Samples/Program.cs
+++ b/Samples/Samples/Program.cs
@@ -1,7 +1,9 @@
-using AngouriMath;
+using AngouriMath;
using AngouriMath.Extensions;
using Microsoft.VisualBasic.CompilerServices;
using System;
+using System.Collections.Generic;
+using static AngouriMath.Entity;
using static AngouriMath.Entity.Number;
using static System.Console;
@@ -23,6 +25,22 @@
//WriteLine("(x - goose) * (x - momo) * (x - quack) * (x - momo * goose * quack)".Expand().SolveEquation("x"));
//WriteLine(Number.Arccosecant(Number.Cosecant(1.2)));
-WriteLine("sin(x) / cos(x)".Simplify());
+//var mat = MathS.Matrices.Matrix(new Entity[,]
+// {
+// { "(x + 0)2", "(x + 1)2", "(x + 2)2" },
+// { "(x + 1)2", "(x + 2)2", "(x + 3)2" },
+// { "(x + 2)2", "(x + 3)2", "(x + 4)2" }
+// }
+// );
+
+//for (int i = -4; i <= 13; i++)
+// WriteLine(((Tensor)mat.Substitute("x", $"{i} / 3")).Determinant());
+//MathS.Settings.MaxExpansionTermCount.Global(10000);
+
+//WriteLine(MathS.TryPolynomial(mat.Determinant().Expand(14), "x", out var res) ? res : null);
+
+WriteLine(MathS.Var("θ"));
+
+//WriteLine("(log(e, x) * (log(e, x) + 1) * x ^ x + x ^ (x - 1)) * x ^ x ^ x".Latexise());
//WriteLine("((2x2 + 10x + 1) ^ (1/5) - (x2 + 10x + 1) ^ (1/7)) ^ 35".Expand().Simplify());
diff --git a/Tests/UnitTests/PatternsTest/SimplifyTest.cs b/Tests/UnitTests/PatternsTest/SimplifyTest.cs
index 8470ee81e..3ba48d69c 100644
--- a/Tests/UnitTests/PatternsTest/SimplifyTest.cs
+++ b/Tests/UnitTests/PatternsTest/SimplifyTest.cs
@@ -148,6 +148,12 @@ [Fact] public void BigSimple1() => AssertSimplifyToString(
[InlineData("arcsin(1 / x)", "arccsc(x)")]
[InlineData("arccos(1 / x)", "arcsec(x)")]
public void TrigTest(string input, string output) => AssertSimplifyToString(input, output);
+
+ [Theory]
+ [InlineData("ln(a) + ln(b)", "ln(a * b)")]
+ [InlineData("ln(a) - ln(b)", "ln(a / b)")]
+ [InlineData("log(2, a) + ln(b)", "log(2, a) + ln(b)")]
+ public void PowerRulesTest(string input, string output) => AssertSimplifyToString(input, output);
}
}