├── .gitattributes ├── .gitignore ├── ComputerAlgebra.Plotting ├── ComputerAlgebra.Plotting.csproj └── Plotting │ ├── Plot.cs │ ├── Series.cs │ └── SeriesCollection.cs ├── ComputerAlgebra.sln ├── ComputerAlgebra ├── ComputerAlgebra.csproj ├── Expression │ ├── Arrow.cs │ ├── Atom.cs │ ├── Binary.cs │ ├── Call.cs │ ├── Constant.cs │ ├── Equal.cs │ ├── Expression.cs │ ├── Functions │ │ ├── ExprFunction.cs │ │ ├── Function.cs │ │ ├── LutFunction.cs │ │ ├── NativeFunction.cs │ │ ├── StmtFunction.cs │ │ └── UnknownFunction.cs │ ├── Matrix.cs │ ├── Power.cs │ ├── Product │ │ ├── Multiply.cs │ │ └── Product.cs │ ├── Sets │ │ ├── FiniteSet.cs │ │ ├── Rings.cs │ │ └── Set.cs │ ├── Substitute.cs │ ├── Sum │ │ ├── Add.cs │ │ ├── LinearCombination.cs │ │ ├── Polynomial.cs │ │ └── Sum.cs │ ├── Unary.cs │ ├── Variable.cs │ └── Visitors │ │ ├── CachedRecursiveVisitor.cs │ │ ├── RecursiveVisitor.cs │ │ └── Visitor.cs ├── Extensions │ ├── AlgebraicEquivalents.cs │ ├── DSolve.cs │ ├── DependsOn.cs │ ├── Differentiate.cs │ ├── Evaluate.cs │ ├── Expand.cs │ ├── Factor.cs │ ├── Integrate.cs │ ├── LaTeX.cs │ ├── LaplaceTransform.cs │ ├── NSolve.cs │ ├── Parse.cs │ ├── PrettyString.cs │ ├── Resolve.cs │ ├── Simplify.cs │ ├── SolutionSet.cs │ ├── Solve.cs │ ├── Substitute.cs │ └── SystemOfEquations.cs ├── LinqCompiler │ ├── CodeGen.cs │ ├── CompileExpression.cs │ ├── CompileExtension.cs │ ├── CompileStatement.cs │ ├── DeclContext.cs │ ├── GlobalExpr.cs │ ├── Module.cs │ └── StandardMath.cs ├── Namespace │ ├── DynamicNamespace.cs │ ├── Global │ │ ├── ExpFunction.cs │ │ ├── GlobalNamespace.cs │ │ ├── IfFunction.cs │ │ ├── LnFunction.cs │ │ └── SqrtFunction.cs │ ├── Namespace.cs │ ├── NamespaceSet.cs │ ├── ObjectNamespace.cs │ └── TypeNamespace.cs ├── Statement │ ├── Assignment.cs │ ├── Block.cs │ ├── BreakContinue.cs │ ├── DoWhile.cs │ ├── For.cs │ ├── If.cs │ ├── Return.cs │ ├── Statement.cs │ ├── Visitors │ │ └── Visitor.cs │ └── While.cs ├── Transform │ ├── AlgebraTransform.cs │ ├── CachedTransform.cs │ ├── LinearTransform.cs │ ├── PatternTransform.cs │ ├── SubstituteTransform.cs │ ├── Transform.cs │ └── TransformSet.cs └── Utils │ ├── BigFloat.cs │ ├── BigIntegerExtensions.cs │ ├── BigRational.cs │ ├── Combinatorics.cs │ ├── CustomAttribute.cs │ ├── DefaultDictionary.cs │ ├── DictionaryExtensions.cs │ ├── EnumerableExtensions.cs │ ├── ExpressionConverter.cs │ ├── LazyExpression.cs │ ├── ListExtensions.cs │ ├── MatchContext.cs │ ├── Real.cs │ ├── RealConverter.cs │ ├── ReferenceEqualityComparer.cs │ └── StringVisitor.cs ├── Console ├── Console.csproj └── Program.cs ├── Demo ├── Intro │ ├── App.config │ ├── Intro.csproj │ └── Program.cs ├── LotkaVolterra │ ├── App.config │ ├── LotkaVolterra.csproj │ └── Program.cs └── LotkaVolterraCpp │ ├── LotkaVolterraCpp.vcxproj │ ├── LotkaVolterraCpp.vcxproj.filters │ └── Main.cpp ├── LICENSE ├── README.md └── Tests ├── Program.cs └── Tests.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # Visual Studio 2015/2017 cache/options directory 20 | .vs/ 21 | 22 | # External tool builders 23 | .externalToolBuilders/ 24 | 25 | # Locally stored "Eclipse launch configurations" 26 | *.launch 27 | 28 | # CDT-specific 29 | .cproject 30 | 31 | # PDT-specific 32 | .buildpath 33 | 34 | 35 | ################# 36 | ## Visual Studio 37 | ################# 38 | 39 | ## Ignore Visual Studio temporary files, build results, and 40 | ## files generated by popular Visual Studio add-ons. 41 | 42 | # User-specific files 43 | *.suo 44 | *.user 45 | *.sln.docstates 46 | 47 | # Build results 48 | [Dd]ebug/ 49 | [Rr]elease/ 50 | *_i.c 51 | *_p.c 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.vspscc 66 | .builds 67 | *.dotCover 68 | 69 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 70 | #packages/ 71 | 72 | # Visual C++ cache files 73 | ipch/ 74 | *.aps 75 | *.ncb 76 | *.opensdf 77 | *.sdf 78 | 79 | # Visual Studio profiler 80 | *.psess 81 | *.vsp 82 | 83 | # ReSharper is a .NET coding add-in 84 | _ReSharper* 85 | 86 | # Installshield output folder 87 | [Ee]xpress 88 | 89 | # DocProject is a documentation generator add-in 90 | DocProject/buildhelp/ 91 | DocProject/Help/*.HxT 92 | DocProject/Help/*.HxC 93 | DocProject/Help/*.hhc 94 | DocProject/Help/*.hhk 95 | DocProject/Help/*.hhp 96 | DocProject/Help/Html2 97 | DocProject/Help/html 98 | 99 | # Click-Once directory 100 | publish 101 | 102 | # Others 103 | [Bb]in 104 | [Oo]bj 105 | sql 106 | TestResults 107 | *.Cache 108 | ClientBin 109 | stylecop.* 110 | ~$* 111 | *.dbmdl 112 | Generated_Code #added for RIA/Silverlight projects 113 | 114 | # Backup & report files from converting an old project file to a newer 115 | # Visual Studio version. Backup files are not needed, because we have git ;-) 116 | _UpgradeReport_Files/ 117 | Backup*/ 118 | UpgradeLog*.XML 119 | 120 | 121 | 122 | ############ 123 | ## Windows 124 | ############ 125 | 126 | # Windows image file caches 127 | Thumbs.db 128 | 129 | # Folder config file 130 | Desktop.ini 131 | 132 | 133 | ############# 134 | ## Python 135 | ############# 136 | 137 | *.py[co] 138 | 139 | # Packages 140 | *.egg 141 | *.egg-info 142 | dist 143 | build 144 | eggs 145 | parts 146 | bin 147 | var 148 | sdist 149 | develop-eggs 150 | .installed.cfg 151 | 152 | # Installer logs 153 | pip-log.txt 154 | 155 | # Unit test / coverage reports 156 | .coverage 157 | .tox 158 | 159 | #Translations 160 | *.mo 161 | 162 | #Mr Developer 163 | .mr.developer.cfg 164 | 165 | # Mac crap 166 | .DS_Store 167 | -------------------------------------------------------------------------------- /ComputerAlgebra.Plotting/ComputerAlgebra.Plotting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Library 4 | net6.0-windows 5 | true 6 | ComputerAlgebra.Plotting 7 | ComputerAlgebra.Plotting 8 | Copyright © 2020 9 | 1.0.0.0 10 | 1.0.0.0 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ComputerAlgebra.Plotting/Plotting/Series.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using Matrix2D = System.Drawing.Drawing2D.Matrix; 6 | 7 | namespace ComputerAlgebra.Plotting 8 | { 9 | public enum PointStyle 10 | { 11 | None, 12 | Square, 13 | Circle, 14 | Cross, 15 | } 16 | 17 | /// 18 | /// A data series. 19 | /// 20 | public abstract class Series 21 | { 22 | private string name = "y[x]"; 23 | public string Name { get { return name; } set { name = value; } } 24 | 25 | private Pen pen = null; 26 | public Pen Pen { get { return pen != null ? pen : Pens.Transparent; } set { pen = value; } } 27 | 28 | protected PointStyle pointStyle = PointStyle.Square; 29 | public PointStyle PointStyle { get { return pointStyle; } set { pointStyle = value; } } 30 | 31 | public abstract List Evaluate(double x0, double x1); 32 | 33 | public void Paint(Matrix2D T, double x0, double x1, Graphics G) 34 | { 35 | Pen pen = Pen; 36 | List points = Evaluate(x0, x1); 37 | foreach (PointF[] i in points) 38 | { 39 | T.TransformPoints(i); 40 | G.DrawLines(pen, i); 41 | } 42 | } 43 | 44 | protected static float ToFloat(double x) 45 | { 46 | if (x > 1e6) 47 | return 1e6f; 48 | else if (x < -1e6) 49 | return -1e6f; 50 | else 51 | return (float)x; 52 | } 53 | } 54 | 55 | /// 56 | /// Data series derived from a lambda function. 57 | /// 58 | public class Function : Series 59 | { 60 | protected Func f; 61 | public Function(Func f) { this.f = f; } 62 | 63 | public override List Evaluate(double x0, double x1) 64 | { 65 | int N = 2048; 66 | 67 | List points = new List(); 68 | 69 | List run = new List(); 70 | for (int i = 0; i <= N; ++i) 71 | { 72 | double x = ((x1 - x0) * i) / N + x0; 73 | float fx = ToFloat(f(x)); 74 | 75 | if (double.IsNaN(fx) || float.IsInfinity(fx)) 76 | { 77 | if (run.Count > 1) 78 | points.Add(run.ToArray()); 79 | run.Clear(); 80 | } 81 | else 82 | { 83 | run.Add(new PointF((float)x, fx)); 84 | } 85 | } 86 | if (run.Count > 1) 87 | points.Add(run.ToArray()); 88 | 89 | return points; 90 | } 91 | } 92 | 93 | /// 94 | /// Explicit point list. 95 | /// 96 | public class Scatter : Series 97 | { 98 | protected PointF[] points; 99 | public Scatter(KeyValuePair[] Points) 100 | { 101 | points = Points.Select(i => new PointF((float)i.Key, ToFloat(i.Value))).ToArray(); 102 | } 103 | 104 | public override List Evaluate(double x0, double x1) 105 | { 106 | return new List() { points.ToArray() }; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ComputerAlgebra/ComputerAlgebra.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | Library 5 | ComputerAlgebra 6 | ComputerAlgebra 7 | Copyright © 2020 8 | 1.0.0.0 9 | 1.0.0.0 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Arrow.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | /// 4 | /// Represents an x -> y expression. 5 | /// 6 | public class Arrow : Binary 7 | { 8 | protected Arrow(Expression L, Expression R) : base(Operator.Arrow, L, R) { } 9 | public static Arrow New(Expression L, Expression R) { return new Arrow(L, R); } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Atom.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// An Expression class that has Atoms = { this }. Atom classes will always compare to other atoms successfully. 9 | /// 10 | public abstract class Atom : Expression 11 | { 12 | protected virtual int TypeRank { get { return 3; } } 13 | 14 | public override sealed IEnumerable Atoms { get { yield return this; } } 15 | 16 | // object 17 | public override abstract int GetHashCode(); 18 | public override int CompareTo(Expression R) 19 | { 20 | if (R is Atom RA) 21 | return TypeRank.CompareTo(RA.TypeRank); 22 | 23 | return base.CompareTo(R); 24 | } 25 | } 26 | 27 | /// 28 | /// Atom with a name. 29 | /// 30 | public class NamedAtom : Atom 31 | { 32 | private ulong cmp; 33 | 34 | private string name; 35 | public string Name { get { return name; } } 36 | 37 | protected NamedAtom(string Name) 38 | { 39 | name = Name; 40 | 41 | // Put the first few characters in an integer for fast comparisons. 42 | cmp = 0; 43 | int length = Marshal.SizeOf(cmp) / 2; 44 | for (int i = 0; i < Math.Min(name.Length, length); ++i) 45 | cmp |= (ulong)name[i] << ((length - i - 1) * 16); 46 | } 47 | 48 | public override int GetHashCode() { return name.GetHashCode(); } 49 | public override bool Equals(Expression E) 50 | { 51 | NamedAtom A = E as NamedAtom; 52 | if (E is null || A is null) 53 | return base.Equals(E); 54 | return Equals(name, A.name); 55 | } 56 | public override int CompareTo(Expression R) 57 | { 58 | if (R is NamedAtom RA) 59 | { 60 | int c = TypeRank.CompareTo(RA.TypeRank); 61 | if (c != 0) return c; 62 | 63 | // Try comparing the first 4 chars of the name. 64 | c = cmp.CompareTo(RA.cmp); 65 | if (c != 0) 66 | return c; 67 | 68 | // First 4 chars match, need to use the full compare. 69 | return String.Compare(name, RA.name, StringComparison.Ordinal); 70 | } 71 | 72 | return base.CompareTo(R); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Constant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | public class Constant : Atom 6 | { 7 | protected Real x; 8 | public Real Value { get { return x; } } 9 | 10 | protected Constant(Real x) { this.x = x; } 11 | 12 | private static readonly Constant NegativeOne = new Constant(-1); 13 | private static readonly Constant Zero = new Constant(0); 14 | private static readonly Constant One = new Constant(1); 15 | 16 | public static Constant New(int x) 17 | { 18 | if (x == -1) return NegativeOne; 19 | if (x == 0) return Zero; 20 | if (x == 1) return One; 21 | return new Constant(x); 22 | } 23 | public static Constant New(double x) { return new Constant(x); } 24 | public static Constant New(decimal x) { return new Constant(x); } 25 | public static Constant New(Real x) { return new Constant(x); } 26 | public static Constant New(bool x) { return x ? One : Zero; } 27 | public static Expression New(object x) 28 | { 29 | if (x is int i) return New(i); 30 | if (x is double dbl) return New(dbl); 31 | if (x is decimal d) return New(d); 32 | if (x is bool b) return New(b); 33 | if (x is Real r) return New(r); 34 | throw new InvalidCastException(); 35 | } 36 | 37 | public override bool IsInteger() { return x.IsInteger(); } 38 | public override bool EqualsZero() { return x.EqualsZero(); } 39 | public override bool EqualsOne() { return x.EqualsOne(); } 40 | public override bool IsFalse() { return EqualsZero(); } 41 | public override bool IsTrue() { return !EqualsZero(); } 42 | 43 | public static implicit operator Real(Constant x) { return x.x; } 44 | 45 | public static implicit operator Constant(Real x) { return Constant.New(x); } 46 | public static implicit operator Constant(BigRational x) { return Constant.New(x); } 47 | public static implicit operator Constant(decimal x) { return Constant.New(x); } 48 | public static implicit operator Constant(double x) { return Constant.New(x); } 49 | public static implicit operator Constant(int x) { return Constant.New(x); } 50 | 51 | // Atom. 52 | protected override int TypeRank { get { return 3; } } 53 | 54 | // object interface. 55 | public override int GetHashCode() { return x.GetHashCode(); } 56 | 57 | // Note that this is *not* an arithmetic comparison, it is a canonicalization ordering. 58 | public override int CompareTo(Expression R) 59 | { 60 | if (R is Constant RC) 61 | return Real.Abs(RC.Value).CompareTo(Real.Abs(Value)); 62 | return base.CompareTo(R); 63 | } 64 | public override bool Equals(Expression E) 65 | { 66 | if (E is Constant C) 67 | return Value.Equals(C.Value); 68 | return base.Equals(E); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Equal.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | /// 4 | /// Represents an x == y expression. 5 | /// 6 | public class Equal : Binary 7 | { 8 | protected Equal(Expression L, Expression R) : base(Operator.Equal, L, R) { } 9 | public static Equal New(Expression L, Expression R) { return new Equal(L, R); } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Functions/ExprFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Function defined by an expression. 9 | /// 10 | public class ExprFunction : Function 11 | { 12 | private Expression body; 13 | /// 14 | /// Get the body of this function. 15 | /// 16 | public Expression Body { get { return body; } } 17 | 18 | private IEnumerable parameters; 19 | public override IEnumerable Parameters { get { return parameters; } } 20 | 21 | private ExprFunction(string Name, Expression Body, IEnumerable Params) : base(Name) 22 | { 23 | if (Body is null) 24 | throw new ArgumentNullException("Body"); 25 | 26 | body = Body; 27 | parameters = Params; 28 | } 29 | 30 | /// 31 | /// Create a new anonymous function defined by an expression. 32 | /// 33 | /// 34 | /// 35 | /// 36 | public static ExprFunction New(Expression Body, IEnumerable Params) { return new ExprFunction("", Body, Params.Buffer()); } 37 | public static ExprFunction New(Expression Body, params Variable[] Params) { return New(Body, Params.AsEnumerable()); } 38 | 39 | /// 40 | /// Create a new named function defined by an expression. 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// 46 | public static ExprFunction New(string Name, Expression Body, IEnumerable Params) { return new ExprFunction(Name, Body, Params.Buffer()); } 47 | public static ExprFunction New(string Name, Expression Body, params Variable[] Params) { return New(Name, Body, Params.AsEnumerable()); } 48 | 49 | public override Expression Call(IEnumerable Args) { return body.Evaluate(parameters.Zip(Args, (a, b) => Arrow.New(a, b))); } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Functions/Function.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Base class for a function. 8 | /// 9 | public abstract class Function : NamedAtom 10 | { 11 | protected Function(string Name) : base(Name) { } 12 | 13 | /// 14 | /// Enumerate the parameters of this function. 15 | /// 16 | public abstract IEnumerable Parameters { get; } 17 | 18 | /// 19 | /// Evaluate this function with the given arguments. 20 | /// 21 | /// 22 | /// 23 | public abstract Expression Call(IEnumerable Args); 24 | public Expression Call(params Expression[] Args) { return Call(Args.AsEnumerable()); } 25 | 26 | /// 27 | /// Check if this function could be called with the given parameters. 28 | /// 29 | /// 30 | /// 31 | public virtual bool CanCall(IEnumerable Args) { return CanCall() && Parameters.Count() == Args.Count(); } 32 | public virtual bool CanCall() { return true; } 33 | 34 | /// 35 | /// Check if a call to this function with the given arguments matches an expression. 36 | /// 37 | /// 38 | /// 39 | /// 40 | /// 41 | public virtual bool CallMatches(IEnumerable Arguments, Expression E, MatchContext Matched) 42 | { 43 | if (E is Call EF) 44 | { 45 | if (!Equals(EF.Target)) 46 | return false; 47 | if (Arguments.Count() != EF.Arguments.Count()) 48 | return false; 49 | 50 | return Matched.TryMatch(() => Arguments.Reverse().Zip(EF.Arguments.Reverse(), (p, e) => p.Matches(e, Matched)).All()); 51 | } 52 | 53 | return false; 54 | } 55 | 56 | /// 57 | /// Check if a call to this function with the given arguments is equal to an expression. 58 | /// 59 | /// 60 | /// 61 | /// 62 | public virtual bool CallEquals(IEnumerable Arguments, Expression E) 63 | { 64 | if (E is Call C) 65 | return Equals(C.Target) && Arguments.SequenceEqual(C.Arguments); 66 | 67 | return false; 68 | } 69 | 70 | /// 71 | /// Substitute the variables into the expressions. 72 | /// 73 | /// 74 | /// 75 | /// 76 | public virtual Expression Substitute(Call C, IDictionary x0, bool IsTransform) 77 | { 78 | return ComputerAlgebra.Call.New(C.Target, C.Arguments.Select(i => i.Substitute(x0, IsTransform))); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Functions/LutFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Function defined by a lookup table with linear interpolation. 9 | /// 10 | public class LutFunction : Function 11 | { 12 | private List parameters; 13 | public override IEnumerable Parameters { get { return parameters; } } 14 | 15 | private SortedDictionary points = new SortedDictionary(); 16 | 17 | private LutFunction(string Name, IEnumerable Points) : base(Name) 18 | { 19 | parameters = new List() { Variable.New("x1") }; 20 | foreach (Arrow i in Points) 21 | points.Add((double)i.Left, (double)i.Right); 22 | } 23 | 24 | public static LutFunction New(string Name, IEnumerable Points) { return new LutFunction(Name, Points); } 25 | 26 | public override Expression Call(IEnumerable Args) 27 | { 28 | double x = (double)Args.Single(); 29 | 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Functions/NativeFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace ComputerAlgebra 7 | { 8 | /// 9 | /// Attribute for disallowing substition through a function. 10 | /// 11 | [AttributeUsage(AttributeTargets.Parameter)] 12 | public class NoSubstitute : Attribute 13 | { 14 | public NoSubstitute() { } 15 | } 16 | 17 | /// 18 | /// Function defined by a native function. 19 | /// 20 | public class NativeFunction : Function 21 | { 22 | private object _this = null; 23 | /// 24 | /// Object instance of which the method is a member of. 25 | /// 26 | public object This { get { return _this; } } 27 | 28 | private MethodInfo method; 29 | /// 30 | /// Method to call to implement a call to this function. 31 | /// 32 | public MethodInfo Method { get { return method; } } 33 | 34 | public override IEnumerable Parameters { get { return Method.GetParameters().Select(i => Variable.New(i.Name)); } } 35 | 36 | protected NativeFunction(string Name, object This, MethodInfo Method) 37 | : base(Name) 38 | { 39 | _this = This; 40 | method = Method; 41 | } 42 | 43 | /// 44 | /// Create a new Function object implemented by a static method. 45 | /// 46 | /// 47 | /// 48 | public static NativeFunction New(MethodInfo Method) { return new NativeFunction(Method.Name, null, Method); } 49 | /// 50 | /// Create a new Function object implemented by a non-static method. 51 | /// 52 | /// 53 | /// 54 | /// 55 | public static NativeFunction New(object This, MethodInfo Method) { return new NativeFunction(Method.Name, This, Method); } 56 | public static NativeFunction New(string Name, object This, MethodInfo Method) { return new NativeFunction(Name, This, Method); } 57 | /// 58 | /// Create a new Function object implemented by a Delegate. 59 | /// 60 | /// 61 | /// 62 | /// 63 | /// 64 | //public static NativeFunction New(string Name, Delegate Method) { return new NativeFunction(Name, Method, Method.GetMethodInfo()); } 65 | public static NativeFunction New(string Name, T Method) { return new NativeFunction(Name, Method, typeof(T).GetMethod("Invoke")); } 66 | 67 | public override Expression Call(IEnumerable Args) 68 | { 69 | if (!Args.Zip(Method.GetParameters(), (a, p) => p.ParameterType.IsAssignableFrom(a.GetType())).All()) 70 | return null; 71 | 72 | try 73 | { 74 | object ret = Method.Invoke(_this, Args.ToArray()); 75 | if (ret is Expression) 76 | return ret as Expression; 77 | else 78 | return Constant.New(ret); 79 | } 80 | catch (TargetInvocationException Ex) 81 | { 82 | throw Ex.InnerException; 83 | } 84 | } 85 | 86 | public override bool CanCall(IEnumerable Args) 87 | { 88 | return Method.GetParameters().Length == Args.Count(); 89 | } 90 | 91 | public override Expression Substitute(Call C, IDictionary x0, bool IsTransform) 92 | { 93 | if (IsTransform) 94 | return base.Substitute(C, x0, IsTransform); 95 | 96 | Dictionary now = new Dictionary(x0); 97 | List late = new List(); 98 | 99 | foreach (var i in method.GetParameters().Zip(C.Arguments, (p, a) => new { p, a })) 100 | { 101 | if (i.p.CustomAttribute() != null) 102 | { 103 | if (now.ContainsKey(i.a)) 104 | { 105 | late.Add(Arrow.New(i.a, now[i.a])); 106 | now.Remove(i.a); 107 | } 108 | } 109 | } 110 | 111 | if (!now.Empty()) 112 | C = ComputerAlgebra.Call.New(C.Target, C.Arguments.Select(i => i.Substitute(now))); 113 | 114 | if (late.Empty()) 115 | return C; 116 | else 117 | return ComputerAlgebra.Substitute.New(C, late.Count > 1 ? (Expression)Set.New(late) : late.Single()); 118 | } 119 | 120 | public override bool Equals(Expression E) 121 | { 122 | if (E is NativeFunction F) 123 | return Equals(method, F.method) && Equals(_this, F._this); 124 | 125 | return base.Equals(E); 126 | } 127 | public override int GetHashCode() { return method.GetHashCode(); } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Functions/StmtFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Function defined by an expression. 9 | /// 10 | public class StmtFunction : Function 11 | { 12 | private Statement body; 13 | /// 14 | /// Get the body of this function. 15 | /// 16 | public Statement Body { get { return body; } } 17 | 18 | private IEnumerable parameters; 19 | public override IEnumerable Parameters { get { return parameters; } } 20 | 21 | private StmtFunction(string Name, Statement Body, IEnumerable Params) 22 | : base(Name) 23 | { 24 | if (Body is null) 25 | throw new ArgumentNullException("Body"); 26 | 27 | body = Body; 28 | parameters = Params; 29 | } 30 | 31 | /// 32 | /// Create a new anonymous function defined by a statement. 33 | /// 34 | /// 35 | /// 36 | /// 37 | public static StmtFunction New(Statement Body, IEnumerable Params) { return new StmtFunction("", Body, Params.Buffer()); } 38 | public static StmtFunction New(Statement Body, params Variable[] Params) { return New(Body, Params.AsEnumerable()); } 39 | 40 | /// 41 | /// Create a new named function defined by a statement. 42 | /// 43 | /// 44 | /// 45 | /// 46 | /// 47 | public static StmtFunction New(string Name, Statement Body, IEnumerable Params) { return new StmtFunction(Name, Body, Params.Buffer()); } 48 | public static StmtFunction New(string Name, Statement Body, params Variable[] Params) { return New(Name, Body, Params.AsEnumerable()); } 49 | 50 | public override Expression Call(IEnumerable Args) 51 | { 52 | try 53 | { 54 | body.Execute(parameters.Zip(Args, (a, b) => Arrow.New(a, b))); 55 | } 56 | catch (ReturnException Ret) 57 | { 58 | return Ret.Value; 59 | } 60 | throw new InvalidOperationException("Return statement not executed."); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Functions/UnknownFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Function yet to be resolved. 8 | /// 9 | public class UnknownFunction : Function 10 | { 11 | private IEnumerable parameters; 12 | public override IEnumerable Parameters { get { return parameters; } } 13 | 14 | private UnknownFunction(string Name, IEnumerable Params) : base(Name) { parameters = Params; } 15 | 16 | public static UnknownFunction New(string Name, IEnumerable Params) { return new UnknownFunction(Name, Params.Buffer()); } 17 | public static UnknownFunction New(string Name, params Variable[] Params) { return New(Name, Params.AsEnumerable()); } 18 | public static UnknownFunction New(string Name, int ParamCount) { return New(Name, Enumerable.Range(0, ParamCount).Select(i => Variable.New("_" + i.ToString()))); } 19 | 20 | public override Expression Call(IEnumerable Args) { throw new UnresolvedName("Call to unknown function '" + Name + "'."); } 21 | 22 | public override bool CanCall() { return false; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Power.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | public class Power : Binary 4 | { 5 | protected Power(Expression L, Expression R) : base(Operator.Power, L, R) { } 6 | public static Expression New(Expression L, Expression R) { return new Power(L, R); } 7 | 8 | /// 9 | /// If x is of the form a^b, return b, otherwise return 1. 10 | /// 11 | /// 12 | /// 13 | public static Expression ExponentOf(Expression x) 14 | { 15 | if (x is Power p) 16 | return p.Right; 17 | return 1; 18 | } 19 | 20 | /// 21 | /// If x is of the form a^n where n is an integer, return n, otherwise return 1. 22 | /// 23 | /// 24 | /// 25 | public static int IntegralExponentOf(Expression x) 26 | { 27 | Expression n = ExponentOf(x); 28 | if (n.IsInteger()) 29 | return (int)(Real)n; 30 | return 1; 31 | } 32 | 33 | /// 34 | /// If x is of the form a^b where b is constant, return b, otherwise return 1. 35 | /// 36 | /// 37 | /// 38 | public static Real ConstantExponentOf(Expression x) 39 | { 40 | Expression n = ExponentOf(x); 41 | if (n is Constant C) 42 | return C.Value; 43 | return 1; 44 | } 45 | 46 | public override bool Matches(Expression E, MatchContext Matched) 47 | { 48 | Expression matched; 49 | if (Matched.TryGetValue(Right, out matched)) 50 | { 51 | if (Left.Matches(E ^ Binary.Divide(1, matched), Matched)) 52 | return true; 53 | } 54 | 55 | // x^0 = 1. 56 | if (E.EqualsOne() && Right.Matches(0, Matched)) 57 | return true; 58 | // 0^x = 0. 59 | if (E.EqualsZero() && Left.Matches(0, Matched)) 60 | return true; 61 | 62 | if (E is Power PE && Matched.TryMatch(() => Left.Matches(PE.Left, Matched) && Right.Matches(PE.Right, Matched))) 63 | return true; 64 | 65 | // If the exponent matches 1, E can match left. 66 | if (Matched.TryMatch(() => Right.Matches(1, Matched) && Left.Matches(E, Matched))) 67 | return true; 68 | 69 | if (Right.IsInteger() && Left.Matches(ComputerAlgebra.Power.New(E, Binary.Divide(1, Right)).Evaluate(), Matched)) 70 | return true; 71 | 72 | return false; 73 | } 74 | 75 | public override int CompareTo(Expression R) 76 | { 77 | if (R is Power RP) 78 | return LexicalCompareTo( 79 | () => Left.CompareTo(RP.Left), 80 | () => Right.CompareTo(RP.Right)); 81 | 82 | return base.CompareTo(R); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Product/Multiply.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Trivial implementation of Product. 9 | /// 10 | class Multiply : Product 11 | { 12 | protected IEnumerable terms; 13 | public override IEnumerable Terms { get { return terms; } } 14 | 15 | private Multiply(IEnumerable Terms) { terms = Terms; } 16 | 17 | /// 18 | /// Create a new product expression in canonical form. 19 | /// 20 | /// The list of terms in the product expression. 21 | /// 22 | public static new Expression New(IEnumerable Terms) 23 | { 24 | // Canonicalize the terms. 25 | List terms = FlattenTerms(Terms).ToList(); 26 | terms.Sort(); 27 | 28 | switch (terms.Count()) 29 | { 30 | case 0: return 1; 31 | case 1: return terms.First(); 32 | default: return new Multiply(terms); 33 | } 34 | } 35 | 36 | private static IEnumerable FlattenTerms(IEnumerable Terms) 37 | { 38 | foreach (Expression i in Terms) 39 | { 40 | if (i is Product product) 41 | foreach (Expression j in FlattenTerms(product.Terms)) 42 | yield return j; 43 | else if (!i.EqualsOne()) 44 | yield return i; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Product/Product.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// List of expressions to multiply. 8 | /// 9 | public abstract class Product : Expression 10 | { 11 | public abstract IEnumerable Terms { get; } 12 | 13 | /// 14 | /// Create a new product expression in canonical form. 15 | /// 16 | /// The list of terms in the product expression. 17 | /// 18 | public static Expression New(IEnumerable Terms) 19 | { 20 | if (Terms.Any()) 21 | return Multiply.New(Terms); 22 | else 23 | return 1; 24 | } 25 | public static Expression New(params Expression[] Terms) { return Multiply.New(Terms); } 26 | 27 | public override bool Matches(Expression E, MatchContext Matched) 28 | { 29 | // if E is zero, any term can match to zero to succeed. 30 | if (E.EqualsZero()) 31 | return Terms.Any(i => i.Matches(0, Matched)); 32 | 33 | // Move the constants in this pattern to E. 34 | IEnumerable PTerms = Terms; 35 | IEnumerable Constants = PTerms.OfType(); 36 | if (Constants.Any()) 37 | { 38 | E = Binary.Divide(E, New(Constants)).Evaluate(); 39 | PTerms = PTerms.Except(Constants, RefComparer); 40 | } 41 | 42 | IEnumerable ETerms = TermsOf(E); 43 | 44 | // Try starting the match at each term of the pattern. 45 | foreach (Expression p in PTerms) 46 | { 47 | // Remaining terms of the pattern. 48 | Expression P = New(PTerms.ExceptUnique(p, RefComparer)); 49 | 50 | // If p is a variable, we have to handle the possibility that more than one term of E might match this term. 51 | if (p is Variable) 52 | { 53 | // Check if p has already been matched. If it has, treat it as a constant and match the rest of the terms. 54 | Expression matched; 55 | if (Matched.TryGetValue(p, out matched)) 56 | { 57 | // p has already been matched. Remove it out of E and match the remainder of the pattern. 58 | if (P.Matches(E / matched, Matched)) 59 | return true; 60 | } 61 | else 62 | { 63 | // Try matching p to the various combinations of the terms of E. 64 | for (int i = 1; i <= ETerms.Count(); ++i) 65 | { 66 | foreach (IEnumerable e in ETerms.Combinations(i)) 67 | if (Matched.TryMatch(() => 68 | p.Matches(New(e), Matched) && 69 | P.Matches(New(ETerms.ExceptUnique(e, RefComparer)), Matched))) 70 | return true; 71 | } 72 | 73 | // Try matching p to identity. 74 | if (Matched.TryMatch(() => p.Matches(1, Matched) && P.Matches(E, Matched))) 75 | return true; 76 | } 77 | } 78 | else 79 | { 80 | // If p is not a variable, try matching it to any of the terms of E. 81 | foreach (Expression e in ETerms) 82 | if (Matched.TryMatch(() => 83 | p.Matches(e, Matched) && 84 | P.Matches(New(ETerms.ExceptUnique(e, RefComparer)), Matched))) 85 | return true; 86 | } 87 | } 88 | 89 | return false; 90 | } 91 | 92 | /// 93 | /// Enumerate the multiplication terms of E. 94 | /// 95 | /// 96 | /// 97 | public static IEnumerable TermsOf(Expression E) 98 | { 99 | if (E is Product M) 100 | return M.Terms; 101 | else 102 | return new Expression[] { E }; 103 | } 104 | 105 | private static bool IsNegative(Expression x) 106 | { 107 | Constant C = TermsOf(x).First() as Constant; 108 | if (C != null) 109 | return (Real)C < 0; 110 | return false; 111 | } 112 | private static bool IsInDenominator(Expression x) 113 | { 114 | if (x is Power power) 115 | return IsNegative(power.Right); 116 | return false; 117 | } 118 | public static Expression Numerator(Expression x) { return Product.New(Product.TermsOf(x).Where(i => !IsInDenominator(i))); } 119 | public static Expression Denominator(Expression x) { return Product.New(Product.TermsOf(x).Where(i => IsInDenominator(i)).Select(i => (Expression)(i ^ -1))); } 120 | 121 | public override int GetHashCode() { return Terms.OrderedHashCode(); } 122 | public override bool Equals(Expression E) 123 | { 124 | if (E is Product P) 125 | return Terms.SequenceEqual(P.Terms); 126 | 127 | return base.Equals(E); 128 | } 129 | 130 | public override IEnumerable Atoms 131 | { 132 | get 133 | { 134 | foreach (Expression i in Terms) 135 | foreach (Atom j in i.Atoms) 136 | yield return j; 137 | } 138 | } 139 | public override int CompareTo(Expression R) 140 | { 141 | if (R is Product P) 142 | return Terms.LexicalCompareTo(P.Terms); 143 | return base.CompareTo(R); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Sets/FiniteSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Represents an unordered collection of elements. 9 | /// 10 | public class FiniteSet : Set 11 | { 12 | protected IEnumerable members; 13 | /// 14 | /// Elements contained in this set. 15 | /// 16 | public override IEnumerable Members { get { return members; } } 17 | 18 | protected FiniteSet(IEnumerable Members) { members = Members; } 19 | 20 | public new static FiniteSet New(IEnumerable Members) { return new FiniteSet(Members.OrderBy(i => i).Distinct().Buffer()); } 21 | 22 | public override bool Contains(Expression x) { return members.Contains(x); } 23 | 24 | public override bool Matches(Expression Expr, MatchContext Matched) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | public override IEnumerable Atoms 30 | { 31 | get 32 | { 33 | foreach (Expression i in members) 34 | foreach (Atom j in i.Atoms) 35 | yield return j; 36 | } 37 | } 38 | public override int CompareTo(Expression R) 39 | { 40 | if (R is FiniteSet RS) 41 | return members.LexicalCompareTo(RS.Members); 42 | return base.CompareTo(R); 43 | } 44 | 45 | public override bool Equals(Expression E) 46 | { 47 | if (E is FiniteSet S) 48 | return Members.SequenceEqual(S.Members); 49 | return base.Equals(E); 50 | } 51 | public override int GetHashCode() { return Members.UnorderedHashCode(); } 52 | 53 | public static FiniteSet Union(FiniteSet A, FiniteSet B) { return FiniteSet.New(A.Members.Union(B.Members)); } 54 | public static FiniteSet Intersection(FiniteSet A, FiniteSet B) { return FiniteSet.New(A.Members.Intersect(B.Members)); } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Sets/Rings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | public abstract class Ring : Set 6 | { 7 | private int order = 0; 8 | // Helper to determine ordering of rings. 9 | protected int Order { get { return order; } } 10 | 11 | protected Ring(int Order) { order = Order; } 12 | 13 | public override sealed IEnumerable Atoms { get { yield break; } } 14 | 15 | public override int CompareTo(Expression R) 16 | { 17 | if (R is Ring RR) 18 | return Order.CompareTo(RR.Order); 19 | return base.CompareTo(R); 20 | } 21 | public override bool Equals(Expression E) 22 | { 23 | if (E is Ring R) 24 | return Order.Equals(R.Order); 25 | return base.Equals(E); 26 | } 27 | public override int GetHashCode() { return Order.GetHashCode(); } 28 | } 29 | 30 | public class Booleans : Ring 31 | { 32 | protected Booleans() : base(0) { } 33 | 34 | private static readonly Booleans instance = new Booleans(); 35 | public static Booleans New() { return instance; } 36 | 37 | public override bool Contains(Expression x) { return x.IsTrue() || x.IsFalse(); } 38 | } 39 | 40 | public class Integers : Ring 41 | { 42 | protected Integers() : base(1) { } 43 | 44 | private static readonly Integers instance = new Integers(); 45 | public static Integers New() { return instance; } 46 | 47 | public override bool Contains(Expression x) { return x.IsInteger(); } 48 | } 49 | 50 | public class Reals : Ring 51 | { 52 | protected Reals() : base(3) { } 53 | 54 | private static readonly Reals instance = new Reals(); 55 | public static Reals New() { return instance; } 56 | 57 | public override bool Contains(Expression x) { return true; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Sets/Set.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Represents an unordered collection of elements. 9 | /// 10 | public abstract class Set : Expression 11 | { 12 | protected Set() { } 13 | 14 | public virtual IEnumerable Members { get { throw new NotImplementedException("Cannot enumerate members of set '" + ToString() + "'"); } } 15 | 16 | public static Set New(IEnumerable Members) { return FiniteSet.New(Members); } 17 | public static Set New(params Expression[] Members) { return FiniteSet.New(Members.AsEnumerable()); } 18 | 19 | public abstract bool Contains(Expression x); 20 | 21 | public override bool Matches(Expression Expr, MatchContext Matched) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | 26 | public static IEnumerable MembersOf(Expression E) 27 | { 28 | if (E is Set S) 29 | return S.Members; 30 | return new[] { E }; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Substitute.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | /// 4 | /// Represents an x : y expression. 5 | /// 6 | public class Substitute : Binary 7 | { 8 | protected Substitute(Expression L, Expression R) : base(Operator.Substitute, L, R) { } 9 | public static Substitute New(Expression L, Expression R) { return new Substitute(L, R); } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Sum/Add.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Trivial implementation of Sum. 9 | /// 10 | class Add : Sum 11 | { 12 | protected IEnumerable terms; 13 | public override IEnumerable Terms { get { return terms; } } 14 | 15 | private Add(IEnumerable Terms) { terms = Terms; } 16 | 17 | /// 18 | /// Create a new sum expression in canonical form. 19 | /// 20 | /// The list of terms in the sum expression. 21 | /// 22 | public static new Expression New(IEnumerable Terms) 23 | { 24 | Debug.Assert(!Terms.Contains(null)); 25 | 26 | // Canonicalize the terms. 27 | List terms = FlattenTerms(Terms).ToList(); 28 | terms.Sort(); 29 | 30 | switch (terms.Count()) 31 | { 32 | case 0: return 0; 33 | case 1: return terms.First(); 34 | default: return new Add(terms); 35 | } 36 | } 37 | 38 | private static IEnumerable FlattenTerms(IEnumerable Terms) 39 | { 40 | foreach (Expression i in Terms) 41 | { 42 | if (i is Sum sum) 43 | foreach (Expression j in FlattenTerms(sum.Terms)) 44 | yield return j; 45 | else if (!i.EqualsZero()) 46 | yield return i; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Sum/LinearCombination.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Decompose an expression into a linear combination of basis variables. 9 | /// 10 | public class LinearCombination : Sum, IEnumerable>, IEnumerable 11 | { 12 | private DefaultDictionary terms = new DefaultDictionary(0); 13 | 14 | public override IEnumerable Terms 15 | { 16 | get 17 | { 18 | foreach (KeyValuePair i in terms) 19 | { 20 | if (i.Value.EqualsOne()) 21 | yield return i.Key; 22 | else if (i.Key.EqualsOne()) 23 | yield return i.Value; 24 | else 25 | yield return Product.New(i.Key, i.Value); 26 | } 27 | } 28 | } 29 | 30 | /// 31 | /// Coefficients of this linear combination. 32 | /// 33 | /// Basis variable to access the coefficient for. 34 | /// 35 | public Expression this[Expression b] { get { return terms[b]; } } 36 | 37 | private LinearCombination() { } 38 | private LinearCombination(IEnumerable> Terms) 39 | { 40 | foreach (KeyValuePair i in Terms) 41 | terms[i.Key] = i.Value; 42 | } 43 | 44 | private void AddTerm(IEnumerable B, Expression t) 45 | { 46 | foreach (Expression b in B) 47 | { 48 | if (Product.TermsOf(t).Count(i => i.Equals(b)) == 1) 49 | { 50 | terms[b] += Product.New(Product.TermsOf(t).Except(b)); 51 | return; 52 | } 53 | } 54 | terms[1] += t; 55 | } 56 | 57 | /// 58 | /// Create a new linear combination expression equal to x. 59 | /// 60 | /// Basis terms for the linear combination. 61 | /// 62 | /// 63 | public static LinearCombination New(IEnumerable B, Expression x) 64 | { 65 | B = B.Buffer(); 66 | 67 | LinearCombination ret = new LinearCombination(); 68 | foreach (Expression t in Sum.TermsOf(x.Expand())) 69 | ret.AddTerm(B, t); 70 | return ret; 71 | } 72 | 73 | /// 74 | /// Createa new linear combination with the given list of terms. 75 | /// 76 | /// 77 | /// 78 | public static LinearCombination New(IEnumerable> Terms) { return new LinearCombination(Terms); } 79 | 80 | // IEnumerable> 81 | public IEnumerator> GetEnumerator() { return terms.GetEnumerator(); } 82 | 83 | IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } 84 | } 85 | } -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Sum/Sum.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Base class for expressions to sum other expressions. 8 | /// 9 | public abstract class Sum : Expression 10 | { 11 | public abstract IEnumerable Terms { get; } 12 | 13 | /// 14 | /// Create a new sum expression in canonical form. 15 | /// 16 | /// The list of terms in the sum expression. 17 | /// 18 | public static Expression New(IEnumerable Terms) 19 | { 20 | if (Terms.Any()) 21 | return Add.New(Terms); 22 | else 23 | return 0; 24 | } 25 | public static Expression New(params Expression[] Terms) { return Add.New(Terms); } 26 | 27 | public override bool Matches(Expression E, MatchContext Matched) 28 | { 29 | // Move the constants in this pattern to E. 30 | IEnumerable PTerms = Terms; 31 | IEnumerable Constants = PTerms.OfType(); 32 | if (Constants.Any()) 33 | { 34 | E = Binary.Subtract(E, New(Constants)).Evaluate(); 35 | PTerms = PTerms.Except(Constants, RefComparer); 36 | } 37 | 38 | IEnumerable ETerms = TermsOf(E); 39 | 40 | // Try starting the match at each term of the pattern. 41 | foreach (Expression p in PTerms) 42 | { 43 | // Remaining terms of the pattern. 44 | Expression P = New(PTerms.ExceptUnique(p, RefComparer)); 45 | 46 | // If p is a variable, we have to handle the possibility that more than one term of E might match this term. 47 | if (p is Variable) 48 | { 49 | // Check if p has already been matched. If it has, treat it as a constant and match the rest of the terms. 50 | Expression matched; 51 | if (Matched.TryGetValue(p, out matched)) 52 | { 53 | // p has already been matched. Remove it out of E and match the remainder of the pattern. 54 | if (P.Matches(Binary.Subtract(E, matched).Evaluate(), Matched)) 55 | return true; 56 | } 57 | else 58 | { 59 | // Try matching p to the various combinations of the terms of E. 60 | for (int i = 1; i <= ETerms.Count(); ++i) 61 | { 62 | foreach (IEnumerable e in ETerms.Combinations(i)) 63 | if (Matched.TryMatch(() => 64 | p.Matches(New(e), Matched) && 65 | P.Matches(New(ETerms.ExceptUnique(e, RefComparer)), Matched))) 66 | return true; 67 | } 68 | 69 | // Try matching p to identity. 70 | if (Matched.TryMatch(() => p.Matches(0, Matched) && P.Matches(E, Matched))) 71 | return true; 72 | } 73 | } 74 | else 75 | { 76 | // If p is not a variable, try matching it to any of the terms of E. 77 | foreach (Expression e in ETerms) 78 | if (Matched.TryMatch(() => 79 | p.Matches(e, Matched) && 80 | P.Matches(New(ETerms.ExceptUnique(e, RefComparer)), Matched))) 81 | return true; 82 | } 83 | } 84 | 85 | return false; 86 | } 87 | 88 | /// 89 | /// Enumerate the addition terms of E. 90 | /// 91 | /// 92 | /// 93 | public static IEnumerable TermsOf(Expression E) 94 | { 95 | if (E is Sum S) 96 | return S.Terms; 97 | else 98 | return new Expression[] { E }; 99 | } 100 | 101 | // object interface. 102 | public override int GetHashCode() { return Terms.OrderedHashCode(); } 103 | public override bool Equals(Expression E) 104 | { 105 | if (E is Sum S) 106 | return Terms.SequenceEqual(S.Terms); 107 | return base.Equals(E); 108 | } 109 | 110 | public override IEnumerable Atoms 111 | { 112 | get 113 | { 114 | foreach (Expression i in Terms) 115 | foreach (Atom j in i.Atoms) 116 | yield return j; 117 | } 118 | } 119 | public override int CompareTo(Expression R) 120 | { 121 | if (R is Sum S) 122 | return Terms.LexicalCompareTo(S.Terms); 123 | return base.CompareTo(R); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Unary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Represents a unary operator expression. 8 | /// 9 | public class Unary : Expression 10 | { 11 | /// 12 | /// Determine if Op is a unary logic operator. 13 | /// 14 | /// 15 | /// 16 | public static bool IsLogic(Operator Op) 17 | { 18 | switch (Op) 19 | { 20 | case Operator.Not: 21 | return true; 22 | default: 23 | return false; 24 | } 25 | } 26 | 27 | protected Operator op; 28 | /// 29 | /// Get the operator type of this unary expression. 30 | /// 31 | public Operator Operator { get { return op; } } 32 | 33 | protected Expression o; 34 | /// 35 | /// Get the operand of this unary expression. 36 | /// 37 | public Expression Operand { get { return o; } } 38 | 39 | /// 40 | /// Determine if this unary expression operator is a logical operation. 41 | /// 42 | public bool IsLogicOp { get { return IsLogic(op); } } 43 | 44 | protected Unary(Operator Op, Expression Operand) { op = Op; o = Operand; } 45 | 46 | /// 47 | /// Create a new prime unary expression. Prime(x) -> x'. 48 | /// 49 | /// 50 | /// 51 | public static Expression Prime(Expression Operand) 52 | { 53 | if (Operand is Call f && f.Arguments.Count() == 1) 54 | return Call.D(f, f.Arguments.First()); 55 | return new Unary(Operator.Prime, Operand); 56 | } 57 | /// 58 | /// Create a new negation unary expression. Negate(x) -> -x. 59 | /// 60 | /// 61 | /// 62 | public static Expression Negate(Expression Operand) { return Product.New(-1, Operand); } 63 | 64 | /// 65 | /// Create a new inverse unary expression. Inverse(x) -> 1/x. 66 | /// 67 | /// 68 | /// 69 | public static Expression Inverse(Expression Operand) { return Binary.Power(Operand, -1); } 70 | 71 | /// 72 | /// Create a new logic not unary expression. Not(x) -> !x. 73 | /// 74 | /// 75 | /// 76 | public static Expression Not(Expression Operand) { return new Unary(Operator.Not, Operand); } 77 | 78 | /// 79 | /// Create a new unary operator expression. 80 | /// 81 | /// 82 | /// 83 | /// 84 | public static Expression New(Operator Op, Expression Operand) 85 | { 86 | switch (Op) 87 | { 88 | case Operator.Prime: return Prime(Operand); 89 | case Operator.Negate: return Negate(Operand); 90 | case Operator.Inverse: return Inverse(Operand); 91 | case Operator.Not: return Not(Operand); 92 | default: return new Unary(Op, Operand); 93 | } 94 | } 95 | 96 | public static string ToStringPrefix(Operator o) 97 | { 98 | switch (o) 99 | { 100 | case Operator.Prime: return ""; 101 | case Operator.Negate: return "-"; 102 | case Operator.Inverse: return ""; 103 | case Operator.Not: return "!"; 104 | default: return ""; 105 | } 106 | } 107 | 108 | public static string ToStringPostfix(Operator o) 109 | { 110 | switch (o) 111 | { 112 | case Operator.Prime: return "'"; 113 | case Operator.Negate: return ""; 114 | case Operator.Inverse: return "^-1"; 115 | case Operator.Not: return ""; 116 | default: return ""; 117 | } 118 | } 119 | 120 | // object interface. 121 | public override int GetHashCode() { return Operator.GetHashCode() ^ Operand.GetHashCode(); } 122 | public override bool Equals(Expression E) 123 | { 124 | if (E is Unary U) 125 | return Operator.Equals(U.Operator) && Operand.Equals(U.Operand); 126 | return base.Equals(E); 127 | } 128 | 129 | // Expression interface. 130 | public override IEnumerable Atoms { get { return Operand.Atoms; } } 131 | public override int CompareTo(Expression R) 132 | { 133 | if (R is Unary RU) 134 | return LexicalCompareTo( 135 | () => Operator.CompareTo(RU.Operator), 136 | () => Operand.CompareTo(RU.Operand)); 137 | 138 | return base.CompareTo(R); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Variable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Variable expression. 7 | /// 8 | public class Variable : NamedAtom 9 | { 10 | private Set ring; 11 | /// 12 | /// Identifies the ring this variable is a member of. 13 | /// 14 | public Set Ring { get { return ring; } } 15 | 16 | protected Variable(string Name, Set Ring) : base(Name) { ring = Ring; } 17 | 18 | /// 19 | /// Create a new variable. 20 | /// 21 | /// 22 | /// 23 | public static Variable New(string Name, Set Ring) { return new Variable(Name, Ring); } 24 | /// 25 | /// Create a new variable. 26 | /// 27 | /// 28 | /// 29 | public static Variable New(string Name) { return new Variable(Name, Reals.New()); } 30 | 31 | public override bool Matches(Expression E, MatchContext Matched) 32 | { 33 | return (Ring is null || Ring.Contains(E)) && Matched.Matches(this, E); 34 | } 35 | 36 | protected override int TypeRank { get { return 1; } } 37 | } 38 | 39 | /// 40 | /// Variable the checks a predicate before matching an expression. 41 | /// 42 | class PatternVariable : Variable 43 | { 44 | protected Func condition; 45 | 46 | private PatternVariable(string Name, Set Ring, Func Condition) : base(Name, Ring) { condition = Condition; } 47 | 48 | /// 49 | /// Create a new variable with a condition callback for matching. 50 | /// 51 | /// 52 | /// 53 | /// A function that should return true if the variable is allowed to match the given Expression. 54 | /// 55 | public static PatternVariable New(string Name, Set Ring, Func Condition) { return new PatternVariable(Name, Ring, Condition); } 56 | public static PatternVariable New(string Name, Func Condition) { return new PatternVariable(Name, Reals.New(), Condition); } 57 | 58 | public override bool Matches(Expression E, MatchContext Matched) 59 | { 60 | if (condition(E)) 61 | return base.Matches(E, Matched); 62 | else 63 | return false; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Visitors/CachedRecursiveVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// RecursiveExpressionVisitor that caches the results of Visit. 7 | /// The cache enables the CachedRecursiveVisitor to detect and avoid stack overflow situations. 8 | /// 9 | public class CachedRecursiveVisitor : RecursiveExpressionVisitor 10 | { 11 | private Dictionary cache = new Dictionary(); 12 | 13 | public override Expression Visit(Expression E) 14 | { 15 | if (cache.TryGetValue(E, out Expression VE)) 16 | return VE; 17 | 18 | return cache[E] = base.Visit(E); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Visitors/RecursiveVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Recursively visit an expression. 7 | /// 8 | public class RecursiveExpressionVisitor : ExpressionVisitor 9 | { 10 | protected override Expression VisitUnknown(Expression E) { return E; } 11 | 12 | protected virtual IEnumerable VisitList(IEnumerable List) 13 | { 14 | List list = new List(); 15 | bool equal = true; 16 | foreach (Expression i in List) 17 | { 18 | Expression Vi = Visit(i); 19 | if (Vi is null) 20 | return null; 21 | list.Add(Vi); 22 | 23 | equal = equal && ReferenceEquals(Vi, i); 24 | } 25 | return equal ? List : list; 26 | } 27 | 28 | protected override Expression VisitBinary(Binary B) 29 | { 30 | Expression L = Visit(B.Left); 31 | Expression R = Visit(B.Right); 32 | if (L is null || R is null) 33 | return null; 34 | 35 | if (ReferenceEquals(L, B.Left) && ReferenceEquals(R, B.Right)) 36 | return B; 37 | else 38 | return Binary.New(B.Operator, L, R); 39 | } 40 | 41 | protected override Expression VisitUnary(Unary U) 42 | { 43 | Expression O = Visit(U.Operand); 44 | if (O is null) 45 | return null; 46 | 47 | if (ReferenceEquals(O, U.Operand)) 48 | return U; 49 | else 50 | return Unary.New(U.Operator, O); 51 | } 52 | 53 | protected override Expression VisitSum(Sum A) 54 | { 55 | IEnumerable terms = VisitList(A.Terms); 56 | if (terms is null) 57 | return null; 58 | return ReferenceEquals(terms, A.Terms) ? A : Sum.New(terms); 59 | } 60 | 61 | protected override Expression VisitProduct(Product M) 62 | { 63 | IEnumerable terms = VisitList(M.Terms); 64 | if (terms is null) 65 | return null; 66 | return ReferenceEquals(terms, M.Terms) ? M : Product.New(terms); 67 | } 68 | 69 | protected override Expression VisitSet(Set S) 70 | { 71 | IEnumerable members = VisitList(S.Members); 72 | if (members is null) 73 | return null; 74 | return ReferenceEquals(members, S.Members) ? S : Set.New(members); 75 | } 76 | 77 | protected override Expression VisitCall(Call F) 78 | { 79 | IEnumerable arguments = VisitList(F.Arguments); 80 | if (arguments is null) 81 | return null; 82 | return ReferenceEquals(arguments, F.Arguments) ? F : Call.New(F.Target, arguments); 83 | } 84 | 85 | protected override Expression VisitMatrix(Matrix A) 86 | { 87 | IEnumerable elements = VisitList(A); 88 | if (elements is null) 89 | return null; 90 | return ReferenceEquals(elements, A) ? A : Matrix.New(A.M, A.N, elements); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ComputerAlgebra/Expression/Visitors/Visitor.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | /// 4 | /// Visits an expression. 5 | /// 6 | /// Result type of the visitor. 7 | public abstract class ExpressionVisitor 8 | { 9 | protected abstract T VisitUnknown(Expression E); 10 | 11 | protected virtual T VisitSum(Sum A) { return VisitUnknown(A); } 12 | protected virtual T VisitProduct(Product M) { return VisitUnknown(M); } 13 | protected virtual T VisitConstant(Constant C) { return VisitUnknown(C); } 14 | protected virtual T VisitVariable(Variable V) { return VisitUnknown(V); } 15 | protected virtual T VisitSet(Set S) { return VisitUnknown(S); } 16 | protected virtual T VisitBinary(Binary B) { return VisitUnknown(B); } 17 | protected virtual T VisitUnary(Unary U) { return VisitUnknown(U); } 18 | protected virtual T VisitPower(Power P) { return VisitBinary(P); } 19 | protected virtual T VisitCall(Call F) { return VisitUnknown(F); } 20 | protected virtual T VisitMatrix(Matrix A) { return VisitUnknown(A); } 21 | 22 | public virtual T Visit(Expression E) 23 | { 24 | if (E is Sum sum) return VisitSum(sum); 25 | else if (E is Product product) return VisitProduct(product); 26 | else if (E is Constant constant) return VisitConstant(constant); 27 | else if (E is Call call) return VisitCall(call); 28 | else if (E is Variable variable) return VisitVariable(variable); 29 | else if (E is Power power) return VisitPower(power); 30 | else if (E is Binary binary) return VisitBinary(binary); 31 | else if (E is Unary unary) return VisitUnary(unary); 32 | else if (E is Matrix matrix) return VisitMatrix(matrix); 33 | else if (E is Set set) return VisitSet(set); 34 | else return VisitUnknown(E); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/AlgebraicEquivalents.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | public static class AlebraicEquivalentsExtension 6 | { 7 | private static List rules = new List() 8 | { 9 | // Functional equivalents. 10 | //new SubstituteTransform("1/x", "x^-1"), 11 | new SubstituteTransform("Sqrt[x]", "x^(1/2)"), 12 | new SubstituteTransform("Log[x, e]", "Ln[x]"), 13 | 14 | // Log. 15 | new SubstituteTransform("Log[1, x]", "0"), 16 | new SubstituteTransform("Log[x, x]", "1"), 17 | new SubstituteTransform("Log[x^y, b]", "y*Log[x, b]"), 18 | new SubstituteTransform("Log[x*y, b]", "Log[x, b] + Log[y, b]"), 19 | 20 | // Exp. 21 | new SubstituteTransform("Sinh[-x]", "-Sinh[x]"), 22 | new SubstituteTransform("Cosh[-x]", "Cosh[x]"), 23 | new SubstituteTransform("Tanh[-x]", "-Tanh[x]"), 24 | new SubstituteTransform("Csch[-x]", "-Csch[x]"), 25 | new SubstituteTransform("Sech[-x]", "Sech[x]"), 26 | new SubstituteTransform("Coth[-x]", "-Coth[x]"), 27 | 28 | new SubstituteTransform("1/Cosh[x]", "Sech[x]"), 29 | new SubstituteTransform("1/Sinh[x]", "Csch[x]"), 30 | new SubstituteTransform("1/Tanh[x]", "Coth[x]"), 31 | new SubstituteTransform("Sinh[x]/Cosh[x]", "Tanh[x]"), 32 | new SubstituteTransform("Sinh[x]*Sech[x]", "Tanh[x]"), 33 | new SubstituteTransform("Cosh[x]*Csch[x]", "Coth[x]"), 34 | 35 | new SubstituteTransform("Cosh[x]^2 - Sinh[x]^2", "1"), 36 | new SubstituteTransform("Sech[x]^2", "1 - Tanh[x]^2"), 37 | new SubstituteTransform("Coth[x]^2", "1 + Csch[x]^2"), 38 | 39 | new SubstituteTransform("Exp[x] - Exp[-x]", "2*Sinh[x]"), 40 | new SubstituteTransform("Exp[x] + Exp[-x]", "2*Cosh[x]"), 41 | new SubstituteTransform("Cosh[x] + Sinh[x]", "Exp[x]"), 42 | new SubstituteTransform("Cosh[x] - Sinh[x]", "Exp[-x]"), 43 | 44 | // Trig. 45 | new SubstituteTransform("Sin[-x]", "-Sin[x]"), 46 | new SubstituteTransform("Cos[-x]", "Cos[x]"), 47 | new SubstituteTransform("Tan[-x]", "-Tan[x]"), 48 | new SubstituteTransform("Csc[-x]", "-Csc[x]"), 49 | new SubstituteTransform("Sec[-x]", "Sec[x]"), 50 | new SubstituteTransform("Cot[-x]", "-Cot[x]"), 51 | 52 | new SubstituteTransform("1/Cos[x]", "Sec[x]"), 53 | new SubstituteTransform("1/Sin[x]", "Csc[x]"), 54 | new SubstituteTransform("1/Tan[x]", "Cot[x]"), 55 | new SubstituteTransform("Sin[x]/Cos[x]", "Tan[x]"), 56 | new SubstituteTransform("Sin[x]*Sec[x]", "Tan[x]"), 57 | new SubstituteTransform("Cos[x]*Csc[x]", "Cot[x]"), 58 | 59 | new SubstituteTransform("Sin[x]^2 + Cos[x]^2", "1"), 60 | new SubstituteTransform("1 + Tan[x]^2", "Sec[x]^2"), 61 | new SubstituteTransform("1 + Cot[x]^2", "Csc[x]^2"), 62 | 63 | //new SubstituteTransform("Exp[i*x]", "Cos[x] + i*Sin[x]"), 64 | //new SubstituteTransform("(Exp[i*x] - Exp[-i*x])/(2*i)", "Sin[x]"), 65 | //new SubstituteTransform("(Exp[i*x] + Exp[-i*x])/2", "Cos[x]"), 66 | }; 67 | 68 | 69 | /// 70 | /// Enumerate the algebraic equivalents of x. 71 | /// 72 | /// 73 | /// How many times to apply the algebraic transformation rules. 74 | /// 75 | public static IEnumerable AlgebraicEquivalents(this Expression x, int Recursion = 3) 76 | { 77 | return AlgebraicEquivalents(x, Recursion, new HashSet()); 78 | } 79 | 80 | private static IEnumerable AlgebraicEquivalents(Expression x, int Recursion, HashSet Enumerated) 81 | { 82 | // Don't enumerate expressions more than once. 83 | if (!Enumerated.Contains(x)) 84 | { 85 | // Enumerate self. 86 | Enumerated.Add(x); 87 | yield return x; 88 | 89 | if (Recursion > 0) 90 | { 91 | foreach (ITransform T in rules) 92 | { 93 | Expression Tx = T.Transform(x); 94 | if (!ReferenceEquals(Tx, x)) 95 | { 96 | foreach (Expression i in AlgebraicEquivalents(Tx, Recursion - 1, Enumerated)) 97 | yield return i; 98 | } 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/DependsOn.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Visitor for finding subexpressions in expressions. Returns null if any of the expressions is found. 8 | /// 9 | class DependsOnVisitor : ExpressionVisitor 10 | { 11 | protected IEnumerable x; 12 | 13 | public DependsOnVisitor(IEnumerable x) { this.x = x; } 14 | 15 | public override bool Visit(Expression E) 16 | { 17 | if (x.Contains(E)) 18 | return true; 19 | return base.Visit(E); 20 | } 21 | 22 | protected override bool VisitBinary(Binary B) 23 | { 24 | return Visit(B.Left) || Visit(B.Right); 25 | } 26 | 27 | protected override bool VisitCall(Call F) 28 | { 29 | return Visit(F.Target) || F.Arguments.Any(i => Visit(i)); 30 | } 31 | 32 | protected override bool VisitMatrix(Matrix A) 33 | { 34 | return A.Any(i => Visit(i)); 35 | } 36 | 37 | protected override bool VisitPower(Power P) 38 | { 39 | return VisitBinary(P); 40 | } 41 | 42 | protected override bool VisitProduct(Product M) 43 | { 44 | return M.Terms.Any(i => Visit(i)); 45 | } 46 | 47 | protected override bool VisitSet(Set S) 48 | { 49 | return S.Members.Any(i => Visit(i)); 50 | } 51 | 52 | protected override bool VisitSum(Sum A) 53 | { 54 | return A.Terms.Any(i => Visit(i)); 55 | } 56 | 57 | protected override bool VisitUnary(Unary U) 58 | { 59 | return Visit(U.Operand); 60 | } 61 | 62 | protected override bool VisitUnknown(Expression E) 63 | { 64 | return false; 65 | } 66 | } 67 | 68 | public static class DependsOnExtension 69 | { 70 | /// 71 | /// Check if f is a function of any variable in x. 72 | /// 73 | /// 74 | /// 75 | /// true if f is a function of any variable in x. 76 | public static bool DependsOn(this Expression f, IEnumerable x) 77 | { 78 | return new DependsOnVisitor(x.Buffer()).Visit(f); 79 | } 80 | public static bool DependsOn(this IEnumerable f, IEnumerable x) 81 | { 82 | DependsOnVisitor V = new DependsOnVisitor(x.Buffer()); 83 | return f.Any(i => V.Visit(i)); 84 | } 85 | 86 | /// 87 | /// Check if f is a function of x. 88 | /// 89 | /// 90 | /// 91 | /// true if f is a function of x. 92 | public static bool DependsOn(this Expression f, params Expression[] x) { return DependsOn(f, x.AsEnumerable()); } 93 | public static bool DependsOn(this IEnumerable f, params Expression[] x) { return DependsOn(f, x.AsEnumerable()); } 94 | 95 | /// 96 | /// Check if f is a function of x. 97 | /// 98 | /// 99 | /// 100 | /// true if f is a function of x. 101 | public static bool DependsOn(this Expression f, Expression x) { return DependsOn(f, Set.MembersOf(x)); } 102 | public static bool DependsOn(this IEnumerable f, Expression x) { return DependsOn(f, Set.MembersOf(x)); } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/Factor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | public static class FactorExtension 8 | { 9 | // Enumerates x, splitting negative constants into a positive constant and -1. 10 | private static IEnumerable FactorsOf(Expression x) 11 | { 12 | foreach (Expression i in Product.TermsOf(x)) 13 | { 14 | if (i is Constant && (Real)i < 0) 15 | { 16 | yield return -1; 17 | yield return Real.Abs((Real)i); 18 | } 19 | else if (i is Power power) 20 | { 21 | yield return i; 22 | yield return power.Left; 23 | } 24 | else 25 | { 26 | yield return i; 27 | } 28 | } 29 | } 30 | 31 | /// 32 | /// Distribute products across sums. 33 | /// 34 | /// 35 | /// 36 | /// 37 | public static Expression Factor(this Expression f, Expression x) 38 | { 39 | // If f is a product, just factor its terms. 40 | if (f is Product product) 41 | return Product.New(product.Terms.Select(i => i.Factor(x))); 42 | 43 | // If if is l^r, factor l and distribute r. 44 | if (f is Power power) 45 | { 46 | Expression l = power.Left.Factor(x); 47 | Expression r = power.Right; 48 | return Product.New(Product.TermsOf(l).Select(i => Power.New(i, r))); 49 | } 50 | 51 | // If f is a polynomial of x, use polynomial factoring methods. 52 | if (f is Polynomial p && (p.Variable.Equals(x) || (x is null))) 53 | return p.Factor(); 54 | 55 | // Try interpreting f as a polynomial of x. 56 | if (!(x is null)) 57 | { 58 | // If f is a polynomial of x, factor it. 59 | try 60 | { 61 | return Polynomial.New(f, x).Factor(); 62 | } 63 | catch (Exception) { } 64 | } 65 | 66 | // Just factor out common sub-expressions. 67 | if (f is Sum s) 68 | { 69 | // Make a list of each terms' products. 70 | List> terms = s.Terms.Select(i => FactorsOf(i).ToList()).ToList(); 71 | 72 | // All of the distinct factors. 73 | IEnumerable factors = terms.SelectMany(i => i.Except(1, -1)).Distinct(); 74 | // Choose the most common factor to factor. 75 | Expression factor = factors.ArgMax(i => terms.Count(j => j.Contains(i))); 76 | // Find the terms that contain the factor. 77 | List> contains = terms.Where(i => i.Contains(factor)).ToList(); 78 | // If more than one term contains the factor, pull it out and factor the resulting expressions (again). 79 | if (contains.Count() > 1) 80 | { 81 | Expression factored = Sum.New(contains.Select(i => Product.New(i.Except(factor)))); 82 | Expression not_factored = Sum.New(terms.Except(contains).Select(i => Product.New(i))); 83 | return Sum.New(Product.New(factor, factored), not_factored).Factor(null); 84 | } 85 | } 86 | return f; 87 | } 88 | 89 | public static Expression Factor(this Expression f) { return Factor(f, null); } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/Integrate.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | 4 | //// Integrals. 5 | //Simplify.AddRange( 6 | // new LinearTransform("I[f, x]", "f", "x"), 7 | 8 | // new SubstituteTransform("I[u*D[u, x], x]", "u^2/2"), 9 | // new SubstituteTransform("I[Exp[u]*D[u, x], x]", "Exp[u]"), 10 | // new SubstituteTransform("I[u^N*D[u, x], x]", "u^(N + 1)/(N + 1)", "IsInteger[N]", "N != -1"), 11 | // new SubstituteTransform("I[u*D[v, x], x]", "u*v - I[v*D[u, x], x]"), 12 | // new SubstituteTransform("I[D[u, x], x]", "u") 13 | // ); 14 | 15 | public static class IntegrateExtension 16 | { 17 | /// 18 | /// Integrate expression with respect to x. 19 | /// 20 | /// 21 | /// 22 | /// 23 | public static Expression Integrate(this Expression f, Expression x) 24 | { 25 | return Call.I(f, x); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/LaTeX.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ComputerAlgebra 7 | { 8 | class LaTeXVisitor : StringVisitor 9 | { 10 | protected override string VisitProduct(Product M) 11 | { 12 | int pr = Parser.Precedence(Operator.Multiply); 13 | 14 | Expression N = Product.Numerator(M); 15 | string minus = ""; 16 | if (IsNegative(N)) 17 | { 18 | minus = "-"; 19 | N = -N; 20 | } 21 | 22 | string n = String.Join(" ", Product.TermsOf(N).Select(i => Visit(i, pr))); 23 | string d = String.Join(" ", Product.TermsOf(Product.Denominator(M)).Select(i => Visit(i, pr))); 24 | 25 | if (d != "1") 26 | return minus + Frac(n, d); 27 | else 28 | return minus + n; 29 | } 30 | 31 | protected override string VisitSum(Sum A) 32 | { 33 | int pr = Parser.Precedence(Operator.Add); 34 | 35 | StringBuilder s = new StringBuilder(); 36 | s.Append(Visit(A.Terms.First(), pr)); 37 | foreach (Expression i in A.Terms.Skip(1)) 38 | { 39 | string si = Visit(i, pr); 40 | 41 | if (si[0] != '-') 42 | s.Append("+"); 43 | s.Append(si); 44 | } 45 | return s.ToString(); 46 | } 47 | 48 | protected override string VisitBinary(Binary B) 49 | { 50 | int pr = Parser.Precedence(B.Operator); 51 | return Visit(B.Left, pr) + ToString(B.Operator) + Visit(B.Right, pr); 52 | } 53 | 54 | protected override string VisitSet(Set S) 55 | { 56 | return @"\{" + String.Join(", ", S.Members.Select(i => Visit(i))) + @"\}"; 57 | } 58 | 59 | protected override string VisitUnary(Unary U) 60 | { 61 | return ToString(U.Operator) + Visit(U.Operand, Parser.Precedence(U.Operator)); 62 | } 63 | 64 | protected override string VisitCall(Call F) 65 | { 66 | // Special case for differentiate. 67 | if (F.Target.Name == "D" && F.Arguments.Count() == 2) 68 | return @"\frac{d}{d" + Visit(F.Arguments.ElementAt(1)) + "}[" + Visit(F.Arguments.ElementAt(0)) + "]"; 69 | 70 | return Escape(F.Target.Name) + @"(" + String.Join(", ", F.Arguments.Select(i => Visit(i))) + @")"; 71 | } 72 | 73 | protected override string VisitPower(Power P) 74 | { 75 | int pr = Parser.Precedence(Operator.Power); 76 | return Visit(P.Left, pr) + "^{" + Visit(P.Right, pr) + "}"; 77 | } 78 | 79 | protected override string VisitConstant(Constant C) 80 | { 81 | return ((Real)C).ToLaTeX(); 82 | } 83 | 84 | protected override string VisitUnknown(Expression E) 85 | { 86 | return Escape(E.ToString()); 87 | } 88 | 89 | private static string ToString(Operator Op) 90 | { 91 | switch (Op) 92 | { 93 | case Operator.Equal: return "="; 94 | case Operator.NotEqual: return @"\neq "; 95 | case Operator.GreaterEqual: return @"\geq "; 96 | case Operator.LessEqual: return @"\leq "; 97 | case Operator.ApproxEqual: return @"\approx "; 98 | case Operator.Arrow: return @"\to "; 99 | default: return Binary.ToString(Op); 100 | } 101 | } 102 | 103 | private static string Frac(string n, string d) 104 | { 105 | if (n.Length <= 2 && d.Length <= 2) 106 | return @"^{n}/_{d}"; 107 | else 108 | return @"\frac{" + n + "}{" + d + "}"; 109 | } 110 | 111 | private static readonly Dictionary EscapeMap = new Dictionary() 112 | { 113 | { '&', @"\&" }, 114 | { '%', @"\%" }, 115 | { '$', @"\$" }, 116 | { '#', @"\#" }, 117 | { '_', @"\_" }, 118 | { '{', @"\{" }, 119 | { '}', @"\}" }, 120 | { '~', @"\textasciitilde" }, 121 | { '^', @"\textasciicircum" }, 122 | { '\\', @"\textbackslash" }, 123 | }; 124 | 125 | private static string Escape(string x) 126 | { 127 | StringBuilder sb = new StringBuilder(); 128 | foreach (char i in x) 129 | { 130 | string e; 131 | if (EscapeMap.TryGetValue(i, out e)) 132 | sb.Append(e); 133 | else 134 | sb.Append(i); 135 | } 136 | return sb.ToString(); 137 | } 138 | } 139 | 140 | public static class LaTeXExtension 141 | { 142 | /// 143 | /// Write x as a LaTeX string. 144 | /// 145 | /// 146 | /// 147 | public static string ToLaTeX(this Expression x) 148 | { 149 | return new LaTeXVisitor().Visit(x); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/LaplaceTransform.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | class LaplaceTransform : LinearTransform 4 | { 5 | // Rule database. 6 | private static TransformSet rules = new TransformSet() 7 | { 8 | new SubstituteTransform("L[D[f, t], t, s]", "s*L[f, t, s] - (f:t->0)"), 9 | new SubstituteTransform("L[I[f, t], t, s]", "L[f, t, s]/s"), 10 | new SubstituteTransform("L[IL[f, s, t], t, s]", "f"), 11 | new SubstituteTransform("L[t^N, t, s]", "Factorial[N]/s^(N + 1)", "IsNatural[N]"), 12 | new SubstituteTransform("L[Exp[a*t], t, s]", "1/(s - a)"), 13 | new SubstituteTransform("L[Sin[a*t], t, s]", "a/(s^2 + a^2)"), 14 | new SubstituteTransform("L[Cos[a*t], t, s]", "s/(s^2 + a^2)") 15 | }; 16 | 17 | protected Expression t, s; 18 | 19 | private LaplaceTransform(Expression t, Expression s) { this.t = t; this.s = s; } 20 | 21 | protected override bool IsConstant(Expression x) { return !x.DependsOn(t); } 22 | 23 | public override Expression Visit(Expression E) 24 | { 25 | if (!E.DependsOn(t)) 26 | return E / s; 27 | 28 | return base.Visit(E); 29 | } 30 | 31 | protected override Expression VisitUnknown(Expression E) 32 | { 33 | Expression LE = Call.L(E, t, s); 34 | 35 | // Try applying a rule to E. 36 | Expression TLE = rules.Transform(LE); 37 | if (!ReferenceEquals(TLE, LE)) 38 | return TLE; 39 | 40 | // Try expanding E. 41 | Expression Ex = E.Expand(s); 42 | if (!Equals(E, Ex)) 43 | return Visit(Ex); 44 | 45 | // Try -L(-E, s, t) 46 | // TODO: It would be nice to handle this case in LinearTransform somehow. 47 | Expression NE = Unary.Negate(E); 48 | Expression NEx = NE.Evaluate(); 49 | if (!Equals(NE, NEx)) 50 | return Unary.Negate(Visit(NEx)); 51 | 52 | // Give up. 53 | return LE; 54 | } 55 | 56 | public static Expression Transform(Expression f, Expression t, Expression s) { return new LaplaceTransform(t, s).Visit(f); } 57 | } 58 | 59 | class InverseLaplaceTransform : LinearTransform 60 | { 61 | // Rule database. 62 | private static TransformSet rules = new TransformSet() 63 | { 64 | new SubstituteTransform("IL[L[f, t, s], s, t]", "f"), 65 | new SubstituteTransform("IL[s*L[f, t, s] - (f:t->0), s, t]", "D[f, t]"), 66 | new SubstituteTransform("IL[L[f, t, s]/s, s, t]", "I[f, t]"), 67 | new SubstituteTransform("IL[1/s^N, s, t]", "t^(N - 1)/Factorial[N - 1]", "IsNatural[N]"), 68 | new SubstituteTransform("IL[1/(s + a), s, t]", "Exp[-a*t]"), 69 | new SubstituteTransform("IL[a/(s^2 + a^2), s, t]", "Sin[a*t]"), 70 | new SubstituteTransform("IL[s/(s^2 + a^2), s, t]", "Cos[a*t]") 71 | }; 72 | 73 | protected Expression s, t; 74 | 75 | private InverseLaplaceTransform(Expression s, Expression t) { this.s = s; this.t = t; } 76 | 77 | protected override bool IsConstant(Expression x) { return !x.DependsOn(s); } 78 | 79 | protected override Expression VisitUnknown(Expression E) 80 | { 81 | Expression LE = Call.IL(E, s, t); 82 | 83 | // Try applying a known rule to E. 84 | Expression TLE = rules.Transform(LE, x => !x.DependsOn(s)); 85 | if (!ReferenceEquals(TLE, LE)) 86 | return TLE; 87 | 88 | // Try expanding E. 89 | Expression Ex = E.Expand(s); 90 | if (!Equals(E, Ex)) 91 | return Visit(Ex); 92 | 93 | // Try -IL(-E, s, t) 94 | // TODO: It would be nice to handle this case in LinearTransform somehow. 95 | Expression NE = Unary.Negate(E); 96 | Expression NEx = NE.Evaluate(); 97 | if (!Equals(NE, NEx)) 98 | return Unary.Negate(Visit(NEx)); 99 | 100 | // Give up. 101 | return LE; 102 | } 103 | 104 | public static Expression Transform(Expression f, Expression s, Expression t) { return new InverseLaplaceTransform(s, t).Visit(f); } 105 | } 106 | 107 | public static class LaplaceTransformExtension 108 | { 109 | /// 110 | /// Compute F(s) = L[f(t)]. 111 | /// 112 | /// 113 | /// 114 | /// 115 | /// 116 | public static Expression LaplaceTransform(this Expression f, Expression t, Expression s) 117 | { 118 | return ComputerAlgebra.LaplaceTransform.Transform(f, t, s); 119 | } 120 | 121 | /// 122 | /// Compute f(t) = L^-1[F(s)] 123 | /// 124 | /// 125 | /// 126 | /// 127 | /// 128 | public static Expression InverseLaplaceTransform(this Expression f, Expression s, Expression t) 129 | { 130 | return ComputerAlgebra.InverseLaplaceTransform.Transform(f, s, t); 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/Resolve.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Expression visitor for performing namespace lookups on unresolved variables/calls. 8 | /// 9 | class ResolveVisitor : RecursiveExpressionVisitor 10 | { 11 | protected Namespace ns; 12 | 13 | public ResolveVisitor(Namespace Namespace) { ns = Namespace; } 14 | 15 | protected override Expression VisitCall(Call F) 16 | { 17 | F = (Call)base.VisitCall(F); 18 | 19 | if (F.Target is UnknownFunction) 20 | { 21 | IEnumerable lookup = ns.LookupFunction(F.Target.Name, F.Arguments); 22 | switch (lookup.Count()) 23 | { 24 | case 0: return F; 25 | case 1: return Call.New(lookup.Single(), F.Arguments); 26 | default: throw new UnresolvedName(F.Target.Name); 27 | } 28 | } 29 | 30 | return F; 31 | } 32 | 33 | protected override Expression VisitVariable(Variable V) 34 | { 35 | IEnumerable lookup = ns.LookupName(V.Name); 36 | switch (lookup.Count()) 37 | { 38 | case 0: return V; 39 | case 1: return lookup.Single(); 40 | default: throw new UnresolvedName(V.Name); 41 | } 42 | } 43 | } 44 | 45 | public static class ResolveExtension 46 | { 47 | /// 48 | /// Substitute variables x0 into f. 49 | /// 50 | /// 51 | /// 52 | /// 53 | public static Expression Resolve(this Expression f, Namespace Namespace) 54 | { 55 | return new ResolveVisitor(Namespace).Visit(f); 56 | } 57 | 58 | /// 59 | /// Substitute variables x0 into f. 60 | /// 61 | /// 62 | /// 63 | /// 64 | public static IEnumerable Resolve(this IEnumerable f, Namespace Namespace) 65 | { 66 | ResolveVisitor V = new ResolveVisitor(Namespace); 67 | return f.Select(i => V.Visit(i)); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/Simplify.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Visitor to estimate the cost of the visited expression for simplification/optimization purposes. 8 | /// 9 | public class CostVisitor : ExpressionVisitor 10 | { 11 | protected override int VisitUnknown(Expression E) { return 1; } 12 | 13 | protected virtual int Cost(Function F) { return 5; } 14 | 15 | protected override int VisitSum(Sum A) { return A.Terms.Sum(i => Visit(i) + 1) - 1; } 16 | protected override int VisitProduct(Product M) { return M.Terms.Sum(i => Visit(i) + 1) - 1; } 17 | protected override int VisitCall(Call F) { return F.Arguments.Sum(i => Visit(i)) + Cost(F.Target); } 18 | protected override int VisitSet(Set S) { return S.Members.Sum(i => Visit(i)); } 19 | 20 | protected override int VisitBinary(Binary B) { return Visit(B.Left) + Visit(B.Right) + 1; } 21 | protected override int VisitUnary(Unary U) { return Visit(U.Operand) + 1; } 22 | } 23 | 24 | /// 25 | /// Find the algebraic equivalent with minimum complexity. 26 | /// 27 | class SimplifyVisitor : EvaluateVisitor 28 | { 29 | protected CostVisitor cost; 30 | 31 | public SimplifyVisitor(CostVisitor Cost) { cost = Cost; } 32 | 33 | public override Expression Visit(Expression E) 34 | { 35 | E = base.Visit(E); 36 | 37 | Expression S = E.AlgebraicEquivalents().ToList().ArgMin(i => cost.Visit(i)); 38 | if (!ReferenceEquals(S, E)) 39 | S = Visit(S); 40 | return S; 41 | } 42 | } 43 | 44 | public static class SimplifyExtension 45 | { 46 | private static CostVisitor complexity = new CostVisitor(); 47 | 48 | /// 49 | /// Simplify expression x. 50 | /// 51 | /// 52 | /// 53 | public static Expression Simplify(this Expression x) { return new SimplifyVisitor(complexity).Visit(x); } 54 | 55 | /// 56 | /// Simplify expression x. 57 | /// 58 | /// 59 | /// 60 | public static Expression Simplify(this Expression x, CostVisitor Cost) { return new SimplifyVisitor(Cost).Visit(x); } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/SolutionSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Represents a set of solutions for a system of equations. 9 | /// 10 | public abstract class SolutionSet 11 | { 12 | /// 13 | /// Enumerate the unknowns solved by this solution set. 14 | /// 15 | public abstract IEnumerable Unknowns { get; } 16 | 17 | /// 18 | /// Check if any of the solutions of this system depend on x. 19 | /// 20 | /// 21 | /// 22 | public abstract bool DependsOn(IEnumerable x); 23 | public bool DependsOn(Expression x) { return DependsOn(new[] { x }); } 24 | } 25 | 26 | /// 27 | /// A simple linear system solution set. The system directly gives solutions. 28 | /// 29 | public class LinearSolutions : SolutionSet 30 | { 31 | private List solutions; 32 | /// 33 | /// Enumerate the solutions to this system. 34 | /// 35 | public IEnumerable Solutions { get { return solutions; } } 36 | 37 | public override IEnumerable Unknowns { get { return solutions.Select(i => i.Left); } } 38 | 39 | public LinearSolutions(IEnumerable Solutions) { solutions = Solutions.ToList(); } 40 | 41 | public override bool DependsOn(IEnumerable x) { return solutions.Any(i => i.Right.DependsOn(x)); } 42 | } 43 | 44 | /// 45 | /// A solution set described by an iteration of newton's method, partially solved. 46 | /// 47 | public class NewtonIteration : SolutionSet 48 | { 49 | private IEnumerable knowns; 50 | /// 51 | /// Enumerate the solved Newton deltas. 52 | /// 53 | public IEnumerable KnownDeltas { get { return knowns; } } 54 | 55 | private IEnumerable unknowns; 56 | /// 57 | /// Enumerate the unkonwn Newton deltas described by Equations. 58 | /// 59 | public IEnumerable UnknownDeltas { get { return unknowns; } } 60 | 61 | /// 62 | /// Enumerate both the solved and unsolved the Newton update deltas in this solution set. 63 | /// 64 | public IEnumerable Deltas { get { return knowns != null ? knowns.Select(i => i.Left).Concat(unknowns) : unknowns; } } 65 | 66 | private IEnumerable equations; 67 | /// 68 | /// Enumerate the equations describing the unsolved part of this system. 69 | /// 70 | public IEnumerable Equations { get { return equations; } } 71 | 72 | private IEnumerable guesses; 73 | /// 74 | /// Initial guesses for the first iteration. 75 | /// 76 | public IEnumerable Guesses { get { return guesses; } } 77 | 78 | public override IEnumerable Unknowns { get { return Deltas.Select(i => DeltaOf(i)); } } 79 | 80 | public NewtonIteration(IEnumerable Equations, IEnumerable Deltas, IEnumerable Guesses) 81 | { 82 | knowns = null; 83 | equations = Equations.Buffer(); 84 | unknowns = Deltas.Buffer(); 85 | guesses = Guesses.Buffer(); 86 | } 87 | 88 | public NewtonIteration(IEnumerable KnownDeltas, IEnumerable Equations, IEnumerable UnknownDeltas, IEnumerable Guesses) 89 | { 90 | knowns = KnownDeltas.Buffer(); 91 | equations = Equations.Buffer(); 92 | unknowns = UnknownDeltas.Buffer(); 93 | guesses = Guesses.Buffer(); 94 | } 95 | 96 | public override bool DependsOn(IEnumerable x) 97 | { 98 | if (knowns != null && knowns.Any(i => i.Right.DependsOn(x))) 99 | return true; 100 | if (guesses != null && guesses.Any(i => i.Right.DependsOn(x))) 101 | return true; 102 | return equations.Any(i => i.DependsOn(x)); 103 | } 104 | 105 | private static Function d = UnknownFunction.New("d", Variable.New("x")); 106 | public static Expression Delta(Expression x) { return Call.New(d, x); } 107 | public static Expression DeltaOf(Expression x) 108 | { 109 | Call c = (Call)x; 110 | if (c.Target == d) 111 | return c.Arguments.First(); 112 | throw new InvalidCastException("Expression is not a Newton Delta"); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/Solve.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Extensions for solving equations. 8 | /// 9 | public static class SolveExtension 10 | { 11 | /// 12 | /// Solve a linear equation or system of linear equations. 13 | /// 14 | /// Equation or set of equations to solve. 15 | /// Variable of set of variables to solve for. 16 | /// The solved values of x, including non-independent solutions. 17 | public static List Solve(this IEnumerable Equations, IEnumerable For) 18 | { 19 | SystemOfEquations S = new SystemOfEquations(Equations, For); 20 | S.RowReduce(); 21 | S.BackSubstitute(); 22 | return S.Solve(); 23 | } 24 | 25 | /// 26 | /// Solve a linear equation or system of linear equations. 27 | /// 28 | /// Equation or set of equations to solve. 29 | /// Variable of set of variables to solve for. 30 | /// The solved values of x, including non-independent solutions. 31 | public static List Solve(this IEnumerable Equations, params Expression[] For) { return Equations.Solve(For.AsEnumerable()); } 32 | 33 | 34 | /// 35 | /// Partially solve a linear equation or system of linear equations. Back substitution is not performed. 36 | /// 37 | /// Equation or set of equations to solve. 38 | /// Variable of set of variables to solve for. 39 | /// The solved values of x, including non-independent solutions. 40 | public static List PartialSolve(this IEnumerable Equations, IEnumerable For) 41 | { 42 | SystemOfEquations S = new SystemOfEquations(Equations, For); 43 | S.RowReduce(); 44 | return S.Solve(); 45 | } 46 | 47 | /// 48 | /// Partially solve a linear equation or system of linear equations. Back substitution is not performed. 49 | /// 50 | /// Equation or set of equations to solve. 51 | /// Variable of set of variables to solve for. 52 | /// The solved values of x, including non-independent solutions. 53 | public static List PartialSolve(this IEnumerable Equations, params Expression[] For) { return Equations.PartialSolve(For.AsEnumerable()); } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ComputerAlgebra/Extensions/Substitute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Expression visitor for substiting expressions in for other expressions. 8 | /// 9 | class SubstituteVisitor : RecursiveExpressionVisitor 10 | { 11 | protected IDictionary x0; 12 | protected bool transform; 13 | 14 | public SubstituteVisitor(IDictionary x0, bool IsTransform) { this.x0 = x0; transform = IsTransform; } 15 | 16 | public override Expression Visit(Expression E) 17 | { 18 | if (x0.TryGetValue(E, out Expression xE)) 19 | return xE; 20 | return base.Visit(E); 21 | } 22 | 23 | protected override Expression VisitCall(Call F) 24 | { 25 | return F.Target.Substitute(F, x0, transform); 26 | } 27 | } 28 | 29 | public static class SubstituteExtension 30 | { 31 | /// 32 | /// Substitute variables x0 into f. 33 | /// 34 | /// 35 | /// 36 | /// 37 | public static Expression Substitute(this Expression f, IDictionary x0, bool IsTransform = false) 38 | { 39 | if (x0.Empty()) 40 | return f; 41 | return new SubstituteVisitor(x0, IsTransform).Visit(f); 42 | } 43 | 44 | /// 45 | /// Evaluate an expression at x = x0. 46 | /// 47 | /// Expression to evaluate. 48 | /// Arrow expressions representing substitutions to evaluate. 49 | /// The evaluated expression. 50 | public static Expression Substitute(this Expression f, IEnumerable x) { return f.Substitute(x.ToDictionary(i => i.Left, i => i.Right)); } 51 | public static Expression Substitute(this Expression f, params Arrow[] x) { return f.Substitute(x.AsEnumerable()); } 52 | 53 | /// 54 | /// Evaluate an expression at x = x0. 55 | /// 56 | /// Expression to evaluate. 57 | /// Variable to evaluate at. 58 | /// Value to evaluate for. 59 | /// The evaluated expression. 60 | public static Expression Substitute(this Expression f, Expression x, Expression x0) { return f.Substitute(new Dictionary { { x, x0 } }); } 61 | 62 | /// 63 | /// Substitute variables x0 into f. 64 | /// 65 | /// 66 | /// 67 | /// 68 | public static IEnumerable Substitute(this IEnumerable f, IDictionary x0, bool IsTransform = false) 69 | { 70 | if (x0.Empty()) 71 | return f; 72 | SubstituteVisitor V = new SubstituteVisitor(x0, IsTransform); 73 | return f.Select(i => V.Visit(i)); 74 | } 75 | 76 | /// 77 | /// Evaluate an expression at x = x0. 78 | /// 79 | /// Expression to evaluate. 80 | /// Arrow expressions representing substitutions to evaluate. 81 | /// The evaluated expression. 82 | public static IEnumerable Substitute(this IEnumerable f, IEnumerable x) { return f.Substitute(x.ToDictionary(i => i.Left, i => i.Right)); } 83 | public static IEnumerable Substitute(this IEnumerable f, params Arrow[] x) { return f.Substitute(x.AsEnumerable()); } 84 | 85 | /// 86 | /// Evaluate an expression at x = x0. 87 | /// 88 | /// Expression to evaluate. 89 | /// Variable to evaluate at. 90 | /// Value to evaluate for. 91 | /// The evaluated expression. 92 | public static IEnumerable Substitute(this IEnumerable f, Expression x, Expression x0) { return f.Substitute(new Dictionary { { x, x0 } }); } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /ComputerAlgebra/LinqCompiler/CompileExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace ComputerAlgebra.LinqCompiler 7 | { 8 | public static class CompileExtension 9 | { 10 | // Compile a function into a CodeGen instance. 11 | private static CodeGen CodeGenFunction(Function f, Module Module) 12 | { 13 | CodeGen code = new CodeGen(Module); 14 | foreach (Variable i in f.Parameters) 15 | code.Decl(Scope.Parameter, i); 16 | if (f is StmtFunction) 17 | { 18 | StmtFunction sf = f as StmtFunction; 19 | code.Compile(sf.Body); 20 | } 21 | else 22 | { 23 | code.Return(code.Compile(f.Call(f.Parameters))); 24 | } 25 | return code; 26 | } 27 | 28 | /// 29 | /// Compile function to a delegate. 30 | /// 31 | /// 32 | /// 33 | public static Delegate Compile(this Function This, Module Module) { return CodeGenFunction(This, Module).Build().Compile(); } 34 | public static Delegate Compile(this Function This) { return Compile(This, null); } 35 | 36 | /// 37 | /// Compile function to a lambda. 38 | /// 39 | /// 40 | /// 41 | public static T Compile(this Function This, Module Module) 42 | { 43 | // Validate the function types match. 44 | MethodInfo invoke = typeof(T).GetMethod("Invoke"); 45 | if (invoke.GetParameters().Count() != This.Parameters.Count()) 46 | throw new InvalidOperationException("Different parameters for Function '" + This.Name + "' and '" + typeof(T).ToString() + "'"); 47 | 48 | // Compile the function. 49 | return CodeGenFunction(This, Module).Build().Compile(); 50 | } 51 | 52 | public static T Compile(this Function This) { return Compile(This, null); } 53 | 54 | /// 55 | /// Compile an expression to a delegate. 56 | /// 57 | /// 58 | /// 59 | /// 60 | /// 61 | public static Delegate Compile(this Expression This, Module Module, IEnumerable Parameters) 62 | { 63 | return ExprFunction.New( 64 | This.Evaluate(Parameters.Select((i, j) => Arrow.New(i, "_" + j.ToString()))), 65 | Parameters.Select((i, j) => Variable.New("_" + j.ToString()))).Compile(Module); 66 | } 67 | public static Delegate Compile(this Expression This, Module Module, params Expression[] Parameters) { return Compile(This, Module, Parameters.AsEnumerable()); } 68 | public static Delegate Compile(this Expression This, IEnumerable Parameters) { return Compile(This, null, Parameters); } 69 | public static Delegate Compile(this Expression This, params Expression[] Parameters) { return Compile(This, Parameters.AsEnumerable()); } 70 | 71 | /// 72 | /// Compile an expression to a delegate. 73 | /// 74 | /// 75 | /// 76 | /// 77 | /// 78 | public static T Compile(this Expression This, Module Module, IEnumerable Parameters) 79 | { 80 | return ExprFunction.New( 81 | This.Evaluate(Parameters.Select((i, j) => Arrow.New(i, "_" + j.ToString()))), 82 | Parameters.Select((i, j) => Variable.New("_" + j.ToString()))).Compile(Module); 83 | } 84 | public static T Compile(this Expression This, Module Module, params Expression[] Parameters) { return Compile(This, Module, Parameters.AsEnumerable()); } 85 | public static T Compile(this Expression This, IEnumerable Parameters) { return Compile(This, null, Parameters); } 86 | public static T Compile(this Expression This, params Expression[] Parameters) { return Compile(This, Parameters.AsEnumerable()); } 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ComputerAlgebra/LinqCompiler/DeclContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using LinqExpr = System.Linq.Expressions.Expression; 4 | using ParamExpr = System.Linq.Expressions.ParameterExpression; 5 | 6 | namespace ComputerAlgebra.LinqCompiler 7 | { 8 | /// 9 | /// Represents a scope in which expressions can be declared and mapped. 10 | /// 11 | public class DeclContext 12 | { 13 | private List decls = new List(); 14 | private Dictionary map = new Dictionary(); 15 | 16 | private DeclContext parent = null; 17 | public DeclContext Parent { get { return parent; } } 18 | 19 | public DeclContext() { } 20 | public DeclContext(DeclContext Parent) { parent = Parent; } 21 | 22 | public IEnumerable Decls { get { return decls; } } 23 | 24 | public void Declare(IEnumerable Decls) { decls.AddRange(Decls); } 25 | public void Declare(Expression Map, LinqExpr To) { map[Map] = To; } 26 | 27 | public virtual LinqExpr LookUp(Expression Expr) 28 | { 29 | LinqExpr expr; 30 | if (map.TryGetValue(Expr, out expr)) 31 | return expr; 32 | 33 | if (parent != null) 34 | return parent.LookUp(Expr); 35 | 36 | return null; 37 | } 38 | 39 | public virtual LinqExpr LookUp(string Name) 40 | { 41 | ParamExpr expr = decls.SingleOrDefault(i => i.Name == Name); 42 | if (expr != null) 43 | return expr; 44 | 45 | if (parent != null) 46 | return parent.LookUp(Name); 47 | 48 | return null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ComputerAlgebra/LinqCompiler/GlobalExpr.cs: -------------------------------------------------------------------------------- 1 | using LinqExpr = System.Linq.Expressions.Expression; 2 | 3 | namespace ComputerAlgebra.LinqCompiler 4 | { 5 | // Holds an instance of T and a LinqExpression that maps to the instance. 6 | public class GlobalExpr 7 | { 8 | private T x; 9 | public T Value { get { return x; } set { x = value; } } 10 | 11 | // A Linq Expression to refer to the voltage at this node. 12 | private LinqExpr expr; 13 | public LinqExpr Expr { get { return expr; } } 14 | 15 | public static implicit operator LinqExpr(GlobalExpr G) { return G.expr; } 16 | 17 | public GlobalExpr() 18 | { 19 | expr = LinqExpr.Field(LinqExpr.Constant(this), typeof(GlobalExpr), "x"); 20 | } 21 | public GlobalExpr(T Init) : this() { x = Init; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ComputerAlgebra/LinqCompiler/Module.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace ComputerAlgebra.LinqCompiler 7 | { 8 | public enum Scope 9 | { 10 | Global, 11 | Local, 12 | Parameter, 13 | Intermediate, 14 | } 15 | 16 | /// 17 | /// Represents the global context of code generation. 18 | /// 19 | public class Module : DeclContext 20 | { 21 | private Dictionary functions = new Dictionary(); 22 | private IEnumerable libraries; 23 | 24 | public Module() : this(null) { } 25 | 26 | public Module(IEnumerable Libraries) 27 | { 28 | if (Libraries == null) 29 | libraries = new Type[] { typeof(StandardMath) }; 30 | else 31 | libraries = Libraries.ToArray(); 32 | } 33 | 34 | /// 35 | /// Declare a new global in this module mapped to expression Map. 36 | /// 37 | /// 38 | /// 39 | /// 40 | public GlobalExpr Declare(Expression Map) 41 | { 42 | GlobalExpr decl = new GlobalExpr(); 43 | base.Declare(Map, decl); 44 | return decl; 45 | } 46 | public GlobalExpr Declare(Expression Map, T Init) 47 | { 48 | GlobalExpr decl = new GlobalExpr(Init); 49 | base.Declare(Map, decl); 50 | return decl; 51 | } 52 | 53 | private MethodInfo LookupFunction(string Name, params Type[] ArgTypes) 54 | { 55 | MethodInfo method = null; 56 | 57 | // Search the libraries for a matching function. 58 | foreach (Type i in libraries) 59 | { 60 | // If the method is not found, check the base type. 61 | for (Type t = i; t != null; t = t.BaseType) 62 | { 63 | MethodInfo m = t.GetMethod(Name, BindingFlags.Static | BindingFlags.Public, null, ArgTypes, null); 64 | if (m != null) 65 | { 66 | // If we already found a method, throw ambiguous. 67 | if (method != null) 68 | throw new AmbiguousMatchException(Name); 69 | method = m; 70 | break; 71 | } 72 | } 73 | } 74 | 75 | return method; 76 | } 77 | 78 | /// 79 | /// Get a function from the type libraries provided to the module. 80 | /// 81 | /// 82 | /// 83 | /// 84 | public MethodInfo GetFunction(string Name, params Type[] ArgTypes) 85 | { 86 | MethodInfo method = LookupFunction(Name, ArgTypes); 87 | if (method != null) 88 | return method; 89 | throw new NotImplementedException("Function '" + Name + "' not found."); 90 | } 91 | 92 | /// 93 | /// Compile the function into this module. 94 | /// 95 | /// 96 | /// 97 | /// 98 | public MethodInfo Compile(Function f, params Type[] ArgTypes) 99 | { 100 | MethodInfo method = null; 101 | 102 | // Search the libraries for a matching function. 103 | method = LookupFunction(f.Name, ArgTypes); 104 | if (method != null) 105 | return method; 106 | 107 | // Try getting the method from the already compiled functions. 108 | if (functions.TryGetValue(f, out method)) 109 | return method; 110 | 111 | // Try compiling f to a new method. 112 | Delegate d = f.Compile(this); 113 | functions[f] = d.Method; 114 | return d.Method; 115 | } 116 | 117 | public MethodInfo Compile(Function f) { return Compile(f, f.Parameters.Select(i => typeof(double)).ToArray()); } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/DynamicNamespace.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Contains functions and values. 7 | /// 8 | public class DynamicNamespace : Namespace 9 | { 10 | private Dictionary> members = new Dictionary>(); 11 | 12 | /// 13 | /// Look up expressions with the given name. 14 | /// 15 | /// 16 | /// 17 | public override IEnumerable LookupName(string Name) 18 | { 19 | List lookup; 20 | if (members.TryGetValue(Name, out lookup)) 21 | return lookup; 22 | return new Expression[] { }; 23 | } 24 | 25 | /// 26 | /// Add a member to the namespace. 27 | /// 28 | /// 29 | public void Add(string Name, Expression x) 30 | { 31 | List values; 32 | if (!members.TryGetValue(Name, out values)) 33 | { 34 | values = new List(); 35 | members[Name] = values; 36 | } 37 | values.Add(x); 38 | } 39 | public void Add(Function f) { Add(f.Name, f); } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/Global/ExpFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Exp function, matches e^x. 9 | /// 10 | public class ExpFunction : NativeFunction 11 | { 12 | private static Variable x = Variable.New("x"); 13 | 14 | private Variable[] parameters = new Variable[] { x }; 15 | public override IEnumerable Parameters { get { return parameters; } } 16 | 17 | private static Constant Exp(Constant x) { return Real.Exp(x); } 18 | private static MethodInfo ExpMethod = typeof(ExpFunction).GetMethod("Exp", BindingFlags.Static | BindingFlags.NonPublic); 19 | 20 | private ExpFunction() : base("Exp", null, ExpMethod) { } 21 | 22 | private static ExpFunction instance = new ExpFunction(); 23 | /// 24 | /// Get an instance of the Exp function. 25 | /// 26 | /// 27 | public static ExpFunction New() { return instance; } 28 | 29 | private static Expression ex = Power.New(Real.e, x); 30 | 31 | public override bool CallMatches(IEnumerable Arguments, Expression E, MatchContext Matched) 32 | { 33 | return base.CallMatches(Arguments, E, Matched) || ex.Matches(E, Matched); 34 | } 35 | 36 | public override bool CallEquals(IEnumerable Arguments, Expression E) 37 | { 38 | if (base.CallEquals(Arguments, E)) 39 | return true; 40 | 41 | MatchContext m = ex.Matches(E); 42 | return m != null && m[x].Equals(Arguments.Single()); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/Global/IfFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// If function. 8 | /// 9 | public class IfFunction : Function 10 | { 11 | private Variable[] parameters = new Variable[] { Variable.New("c"), Variable.New("t"), Variable.New("f") }; 12 | public override IEnumerable Parameters { get { return parameters; } } 13 | 14 | private IfFunction() : base("If") { } 15 | 16 | private static IfFunction instance = new IfFunction(); 17 | /// 18 | /// Get an instance of the If function. 19 | /// 20 | /// 21 | public static IfFunction New() { return instance; } 22 | 23 | public override Expression Call(IEnumerable Args) 24 | { 25 | Expression[] args = Args.ToArray(); 26 | 27 | // If both branches are equal, just return one of them as the result. 28 | if (args[1].Equals(args[2])) 29 | return args[1]; 30 | 31 | // Try to evaluate the condition. 32 | if (args[0].IsTrue()) 33 | return args[1]; 34 | else if (args[0].IsFalse()) 35 | return args[2]; 36 | 37 | // Couldn't evaluate with these arguments. 38 | return null; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/Global/LnFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Sqrt function, matches x^0.5. 9 | /// 10 | public class LnFunction : NativeFunction 11 | { 12 | private static MethodInfo LnMethod = typeof(LnFunction).GetMethod("Ln", BindingFlags.Static | BindingFlags.NonPublic); 13 | private static MethodInfo LogMethod = typeof(GlobalNamespace).GetMethod("Log", BindingFlags.Static | BindingFlags.Public); 14 | private static Variable x = Variable.New("x"); 15 | 16 | private Variable[] parameters = new Variable[] { x }; 17 | public override IEnumerable Parameters { get { return parameters; } } 18 | 19 | private static Constant Ln(Constant x) { return Real.Ln(x); } 20 | 21 | private LnFunction() : base("Ln", null, LnMethod) { } 22 | 23 | private static LnFunction instance = new LnFunction(); 24 | /// 25 | /// Get an instance of the Ln function. 26 | /// 27 | /// 28 | public static LnFunction New() { return instance; } 29 | 30 | private static Expression LogE = ComputerAlgebra.Call.New(NativeFunction.New(LogMethod), x, Real.e); 31 | 32 | public override bool CallMatches(IEnumerable Arguments, Expression E, MatchContext Matched) 33 | { 34 | return base.CallMatches(Arguments, E, Matched) || LogE.Matches(E, Matched); 35 | } 36 | 37 | public override bool CallEquals(IEnumerable Arguments, Expression E) 38 | { 39 | if (base.CallEquals(Arguments, E)) 40 | return true; 41 | 42 | MatchContext m = LogE.Matches(E); 43 | return m != null && m[x].Equals(Arguments.Single()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/Global/SqrtFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Sqrt function, matches x^0.5. 9 | /// 10 | public class SqrtFunction : NativeFunction 11 | { 12 | private static MethodInfo SqrtMethod = typeof(SqrtFunction).GetMethod("Sqrt", BindingFlags.Static | BindingFlags.NonPublic); 13 | private static Variable x = Variable.New("x"); 14 | 15 | private Variable[] parameters = new Variable[] { x }; 16 | public override IEnumerable Parameters { get { return parameters; } } 17 | 18 | private static Constant Sqrt(Constant x) { return Real.Sqrt(x); } 19 | 20 | private SqrtFunction() : base("Sqrt", null, SqrtMethod) { } 21 | 22 | private static SqrtFunction instance = new SqrtFunction(); 23 | /// 24 | /// Get an instance of the Sqrt function. 25 | /// 26 | /// 27 | public static SqrtFunction New() { return instance; } 28 | 29 | private static Expression PowHalf = Power.New(x, 0.5); 30 | 31 | public override bool CallMatches(IEnumerable Arguments, Expression E, MatchContext Matched) 32 | { 33 | return base.CallMatches(Arguments, E, Matched) || PowHalf.Matches(E, Matched); 34 | } 35 | 36 | public override bool CallEquals(IEnumerable Arguments, Expression E) 37 | { 38 | if (base.CallEquals(Arguments, E)) 39 | return true; 40 | 41 | MatchContext m = PowHalf.Matches(E); 42 | return m != null && m[x].Equals(Arguments.Single()); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/Namespace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Exception for bad function/variable lookups. 9 | /// 10 | public class UnresolvedName : Exception 11 | { 12 | public UnresolvedName(string Name) : base("Unresolved name '" + Name + "'.") { } 13 | } 14 | 15 | /// 16 | /// Contains functions and values. 17 | /// 18 | public abstract class Namespace 19 | { 20 | /// 21 | /// Look up expressions with the given name. 22 | /// 23 | /// 24 | /// 25 | public abstract IEnumerable LookupName(string Name); 26 | 27 | /// 28 | /// Lookup functions with the given name and matching parameters. 29 | /// 30 | /// 31 | /// 32 | /// 33 | public IEnumerable LookupFunction(string Name, IEnumerable Params) { return LookupName(Name).OfType().Where(i => i.CanCall(Params)); } 34 | public IEnumerable LookupFunction(string Name, params Expression[] Params) { return LookupFunction(Name, Params.AsEnumerable()); } 35 | 36 | /// 37 | /// Resolve a name to an expression. 38 | /// 39 | /// 40 | /// 41 | public Expression Resolve(string Name) 42 | { 43 | Expression resolved = LookupName(Name).SingleOrDefault(); 44 | if (!(resolved is null)) 45 | return resolved; 46 | throw new UnresolvedName(Name); 47 | } 48 | /// 49 | /// Resolve a name with arguments to a function. 50 | /// 51 | /// 52 | /// 53 | /// 54 | public Function Resolve(string Name, IEnumerable Params) 55 | { 56 | Function resolved = LookupFunction(Name, Params).SingleOrDefault(); 57 | if (!(resolved is null)) 58 | return resolved; 59 | throw new UnresolvedName(Name); 60 | } 61 | public Function Resolve(string Name, params Expression[] Params) { return Resolve(Name, Params.AsEnumerable()); } 62 | 63 | private static GlobalNamespace global = new GlobalNamespace(); 64 | /// 65 | /// Get the global namespace. 66 | /// 67 | public static Namespace Global { get { return global; } } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/NamespaceSet.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Namespace implementation composing a set of other namespaces. 8 | /// 9 | public class NamespaceSet : Namespace 10 | { 11 | private List set; 12 | 13 | public NamespaceSet(IEnumerable Set) { set = Set.ToList(); } 14 | public NamespaceSet(params Namespace[] Set) : this(Set.AsEnumerable()) { } 15 | 16 | public override IEnumerable LookupName(string Name) { return set.SelectMany(i => i.LookupName(Name)); } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/ObjectNamespace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace ComputerAlgebra 7 | { 8 | /// 9 | /// Namespace implementation that performs lookups by reflecting a .Net type or object. 10 | /// 11 | public class ObjectNamespace : Namespace 12 | { 13 | private object _this; 14 | 15 | public ObjectNamespace(object This) { _this = This; } 16 | 17 | public override IEnumerable LookupName(string Name) 18 | { 19 | BindingFlags binding = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; 20 | 21 | Type type = _this.GetType(); 22 | foreach (MethodInfo i in type.GetMethods(binding).Where(i => i.Name == Name)) 23 | yield return NativeFunction.New(_this, i); 24 | foreach (FieldInfo i in type.GetFields(binding).Where(i => i.Name == Name && i.FieldType.IsAssignableFrom(typeof(Expression)))) 25 | yield return (Expression)i.GetValue(_this); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ComputerAlgebra/Namespace/TypeNamespace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace ComputerAlgebra 7 | { 8 | /// 9 | /// Namespace implementation that performs lookups by reflecting a .Net type or object. 10 | /// 11 | public class TypeNamespace : Namespace 12 | { 13 | private Type type; 14 | 15 | public TypeNamespace(Type T) { type = T; } 16 | 17 | public override IEnumerable LookupName(string Name) 18 | { 19 | BindingFlags binding = BindingFlags.Public | BindingFlags.Static; 20 | 21 | foreach (MethodInfo i in type.GetMethods(binding).Where(i => i.Name == Name)) 22 | yield return NativeFunction.New(i); 23 | foreach (FieldInfo i in type.GetFields(binding).Where(i => i.Name == Name && i.FieldType.IsAssignableFrom(typeof(Expression)))) 24 | yield return (Expression)i.GetValue(null); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/Assignment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Exception thrown when the compiler encounters an undefined variable. 8 | /// 9 | public class UndefinedVariable : UnresolvedName 10 | { 11 | public UndefinedVariable(string Name) : base(Name) { } 12 | } 13 | 14 | /// 15 | /// Assignment statement base. 16 | /// 17 | public abstract class BaseAssignment : Statement 18 | { 19 | private Expression assign; 20 | /// 21 | /// Expression to assign to. 22 | /// 23 | public Expression Assign { get { return assign; } } 24 | 25 | protected BaseAssignment(Expression Assign) { assign = Assign; } 26 | } 27 | 28 | /// 29 | /// Represents the assignment of a variable to an expression. 30 | /// 31 | public class Assignment : BaseAssignment 32 | { 33 | private Operator op; 34 | /// 35 | /// Operator to use for assignment. 36 | /// 37 | public Operator Operator { get { return op; } } 38 | 39 | private Expression value; 40 | /// 41 | /// Value to assign. 42 | /// 43 | public Expression Value { get { return value; } } 44 | 45 | private Assignment(Expression Assign, Operator Operator, Expression Value) : base(Assign) { op = Operator; value = Value; } 46 | 47 | /// 48 | /// Create a new assignment. 49 | /// 50 | /// 51 | /// 52 | /// 53 | /// 54 | public static Assignment New(Expression Assign, Operator Operator, Expression Value) { return new Assignment(Assign, Operator, Value); } 55 | public static Assignment New(Expression Assign, Expression Value) { return new Assignment(Assign, Operator.Equal, Value); } 56 | /// 57 | /// Create a new assignment from an arrow expression. The left side of the arrow must be a variable. 58 | /// 59 | /// 60 | /// 61 | public static Assignment New(Arrow Assign) { return New(Assign.Left, Assign.Right); } 62 | 63 | public override void Execute(Dictionary State) 64 | { 65 | Expression value = Value.Evaluate(State); 66 | 67 | try 68 | { 69 | switch (op) 70 | { 71 | case Operator.Add: State[Assign] += value; break; 72 | case Operator.Subtract: State[Assign] -= value; break; 73 | case Operator.Multiply: State[Assign] *= value; break; 74 | case Operator.Divide: State[Assign] /= value; break; 75 | case Operator.Power: State[Assign] ^= value; break; 76 | case Operator.And: State[Assign] &= value; break; 77 | case Operator.Or: State[Assign] |= value; break; 78 | case Operator.Equal: State[Assign] = value; break; 79 | 80 | default: throw new NotImplementedException("Operator not implemented for assignment."); 81 | } 82 | } 83 | catch (KeyNotFoundException) { throw new UndefinedVariable(Assign.ToString()); } 84 | } 85 | } 86 | 87 | /// 88 | /// Represents incrementing and assigning a variable. 89 | /// 90 | public class Increment : BaseAssignment 91 | { 92 | private Increment(Expression Assign) : base(Assign) { } 93 | 94 | public static Increment New(Expression Assign) { return new Increment(Assign); } 95 | 96 | public override void Execute(Dictionary State) 97 | { 98 | State[Assign] += 1; 99 | } 100 | } 101 | 102 | /// 103 | /// Represents decrementing and assigning a variable. 104 | /// 105 | public class Decrement : BaseAssignment 106 | { 107 | private Decrement(Expression Assign) : base(Assign) { } 108 | 109 | public static Decrement New(Expression Assign) { return new Decrement(Assign); } 110 | 111 | public override void Execute(Dictionary State) 112 | { 113 | State[Assign] += 1; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/Block.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Represents an ordered sequence of child statements. 7 | /// 8 | public class Block : Statement 9 | { 10 | private IEnumerable statements; 11 | /// 12 | /// Enumerates the sequence of child statements in this block. 13 | /// 14 | public IEnumerable Statements { get { return statements; } } 15 | 16 | private Block(IEnumerable Statements) { statements = Statements; } 17 | 18 | /// 19 | /// Create a new block. 20 | /// 21 | /// 22 | /// 23 | public static Block New(IEnumerable Statements) { return new Block(Statements.Buffer()); } 24 | public static Block New(params Statement[] Statements) { return new Block(Statements); } 25 | 26 | public override void Execute(Dictionary State) 27 | { 28 | foreach (Statement i in statements) 29 | i.Execute(State); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/BreakContinue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Exception thrown when a break statement is encountered. 8 | /// 9 | public class BreakException : Exception { public BreakException() { } } 10 | 11 | /// 12 | /// Exception thrown when a continue statement is encountered. 13 | /// 14 | public class ContinueException : Exception { public ContinueException() { } } 15 | 16 | /// 17 | /// Represents a break statement. 18 | /// 19 | public class Break : Statement 20 | { 21 | private Break() { } 22 | 23 | private static Break instance = new Break(); 24 | 25 | /// 26 | /// Create a new block. 27 | /// 28 | /// 29 | /// 30 | public static Break New() { return instance; } 31 | 32 | public override void Execute(Dictionary State) { throw new BreakException(); } 33 | } 34 | 35 | /// 36 | /// Represents a continue statement. 37 | /// 38 | public class Continue : Statement 39 | { 40 | private Continue() { } 41 | 42 | private static Continue instance = new Continue(); 43 | 44 | /// 45 | /// Create a new block. 46 | /// 47 | /// 48 | /// 49 | public static Continue New() { return instance; } 50 | 51 | public override void Execute(Dictionary State) { throw new ContinueException(); } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/DoWhile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Represents a do { body } while(cond) loop. 7 | /// 8 | public class DoWhile : Statement 9 | { 10 | private Expression cond; 11 | /// 12 | /// Condition to test. 13 | /// 14 | public Expression Condition { get { return cond; } } 15 | 16 | private Statement body; 17 | /// 18 | /// Body of the loop. 19 | /// 20 | public Statement Body { get { return body; } } 21 | 22 | private DoWhile(Statement Body, Expression Condition) { body = Body; cond = Condition; } 23 | 24 | /// 25 | /// Create a new while loop. 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static DoWhile New(Statement Body, Expression Condition) { return new DoWhile(Body, Condition); } 31 | public static DoWhile New(Statement Body) { return new DoWhile(Body, null); } 32 | 33 | public override void Execute(Dictionary State) 34 | { 35 | do 36 | { 37 | try 38 | { 39 | body.Execute(State); 40 | } 41 | catch (BreakException) { break; } 42 | catch (ContinueException) { continue; } 43 | } while (cond.Evaluate(State)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/For.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Represents a for({ init }, cond, { next }) { body } loop. 7 | /// 8 | public class For : Statement 9 | { 10 | private Statement init; 11 | /// 12 | /// Initialization statement. 13 | /// 14 | public Statement Init { get { return init; } } 15 | 16 | private Expression cond; 17 | /// 18 | /// Condition to test. 19 | /// 20 | public Expression Condition { get { return cond; } } 21 | 22 | private Statement step; 23 | /// 24 | /// Statement executed after each loop iteration. 25 | /// 26 | public Statement Step { get { return step; } } 27 | 28 | private Statement body; 29 | /// 30 | /// Body of the loop. 31 | /// 32 | public Statement Body { get { return body; } } 33 | 34 | private For(Statement Init, Expression Condition, Statement Step, Statement Body) 35 | { 36 | init = Init; 37 | cond = Condition; 38 | step = Step; 39 | body = Body; 40 | } 41 | 42 | /// 43 | /// Create a new for loop. 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | /// 50 | public static For New(Statement Init, Expression Condition, Statement Step, Statement Body) { return new For(Init, Condition, Step, Body); } 51 | 52 | public override void Execute(Dictionary State) 53 | { 54 | for (init.Execute(State); cond.Evaluate(State); step.Execute(State)) 55 | { 56 | try 57 | { 58 | body.Execute(State); 59 | } 60 | catch (BreakException) { break; } 61 | catch (ContinueException) { continue; } 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/If.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Represents an if(cond) { true } else { false } statement. 7 | /// 8 | public class If : Statement 9 | { 10 | private Expression cond; 11 | /// 12 | /// Condition to test. 13 | /// 14 | public Expression Condition { get { return cond; } } 15 | 16 | private Statement _true, _false; 17 | /// 18 | /// Statements for each branch of the condition. 19 | /// 20 | public Statement True { get { return _true; } } 21 | public Statement False { get { return _false; } } 22 | 23 | private If(Expression Condition, Statement True, Statement False) { cond = Condition; _true = True; _false = False; } 24 | 25 | /// 26 | /// Create a new if statement. 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | public static If New(Expression Condition, Statement True, Statement False) { return new If(Condition, True, False); } 33 | public static If New(Expression Condition, Statement True) { return new If(Condition, True, null); } 34 | 35 | public override void Execute(Dictionary State) 36 | { 37 | if (cond.Evaluate(State)) 38 | _true.Execute(State); 39 | else 40 | _false.Execute(State); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/Return.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Exception thrown when a return statement is encountered. 8 | /// 9 | public class ReturnException : Exception 10 | { 11 | private Expression value; 12 | public Expression Value { get { return value; } } 13 | 14 | public ReturnException(Expression Value) { value = Value; } 15 | } 16 | 17 | /// 18 | /// Represents an ordered sequence of child statements. 19 | /// 20 | public class Return : Statement 21 | { 22 | private Expression value; 23 | public Expression Value { get { return value; } } 24 | 25 | private Return(Expression Value) { value = Value; } 26 | 27 | /// 28 | /// Create a new block. 29 | /// 30 | /// 31 | /// 32 | public static Return New(Expression Value) { return new Return(Value); } 33 | public static Return New() { return new Return(null); } 34 | 35 | public override void Execute(Dictionary State) 36 | { 37 | throw new ReturnException(Value.Evaluate(State)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/Statement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | public abstract class Statement 7 | { 8 | public abstract void Execute(Dictionary State); 9 | public void Execute(IEnumerable State) { Execute(State.ToDictionary(i => i.Left, i => i.Right)); } 10 | 11 | public static implicit operator Statement(Arrow Assign) { return Assignment.New(Assign); } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/Visitors/Visitor.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | /// 4 | /// Visits a statement. 5 | /// 6 | /// Result type of the visitor. 7 | public abstract class StatementVisitor 8 | { 9 | protected abstract T VisitUnknown(Statement S); 10 | 11 | protected virtual T VisitAssignment(Assignment A) { return VisitUnknown(A); } 12 | protected virtual T VisitIncrement(Increment A) { return VisitUnknown(A); } 13 | protected virtual T VisitDecrement(Decrement D) { return VisitUnknown(D); } 14 | protected virtual T VisitBlock(Block B) { return VisitUnknown(B); } 15 | protected virtual T VisitFor(For F) { return VisitUnknown(F); } 16 | protected virtual T VisitIf(If I) { return VisitUnknown(I); } 17 | protected virtual T VisitWhile(While W) { return VisitUnknown(W); } 18 | protected virtual T VisitDoWhile(DoWhile W) { return VisitUnknown(W); } 19 | protected virtual T VisitReturn(Return R) { return VisitUnknown(R); } 20 | protected virtual T VisitBreak(Break B) { return VisitUnknown(B); } 21 | protected virtual T VisitContinue(Continue C) { return VisitUnknown(C); } 22 | 23 | public virtual T Visit(Statement S) 24 | { 25 | if (S is Assignment) return VisitAssignment(S as Assignment); 26 | if (S is Increment) return VisitIncrement(S as Increment); 27 | if (S is Decrement) return VisitDecrement(S as Decrement); 28 | if (S is Block) return VisitBlock(S as Block); 29 | if (S is For) return VisitFor(S as For); 30 | if (S is If) return VisitIf(S as If); 31 | if (S is While) return VisitWhile(S as While); 32 | if (S is DoWhile) return VisitDoWhile(S as DoWhile); 33 | if (S is Return) return VisitReturn(S as Return); 34 | if (S is Break) return VisitBreak(S as Break); 35 | if (S is Continue) return VisitContinue(S as Continue); 36 | return VisitUnknown(S); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ComputerAlgebra/Statement/While.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Represents a while(cond) { body } loop. 7 | /// 8 | public class While : Statement 9 | { 10 | private Expression cond; 11 | /// 12 | /// Condition to test. 13 | /// 14 | public Expression Condition { get { return cond; } } 15 | 16 | private Statement body; 17 | /// 18 | /// Body of the loop. 19 | /// 20 | public Statement Body { get { return body; } } 21 | 22 | private While(Expression Condition, Statement Body) { cond = Condition; body = Body; } 23 | 24 | /// 25 | /// Create a new while loop. 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static While New(Expression Condition, Statement Body) { return new While(Condition, Body); } 31 | public static While New(Statement Body) { return new While(null, Body); } 32 | 33 | public override void Execute(Dictionary State) 34 | { 35 | while (cond.Evaluate(State)) 36 | { 37 | try 38 | { 39 | body.Execute(State); 40 | } 41 | catch (BreakException) { break; } 42 | catch (ContinueException) { continue; } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ComputerAlgebra/Transform/AlgebraTransform.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Enumerates a set of equivalent algebraic pattern transformations via inverses of basic algebraic operations. 7 | /// TODO: Adding to target is a workaround for bug https://connect.microsoft.com/VisualStudio/feedback/details/677532/an-attempt-was-made-to-load-a-program-with-an-incorrect-format-exception-from-hresult-0x8007000b#details 8 | /// 9 | class AlgebraTransformGenerator : ExpressionVisitor 10 | { 11 | protected Stack equal = new Stack(); 12 | protected Stack conditions; 13 | 14 | protected TransformSet target; 15 | 16 | public AlgebraTransformGenerator(Expression EqualTo, IEnumerable Conditions, TransformSet Target) 17 | { 18 | equal.Push(EqualTo); 19 | conditions = new Stack(Conditions); 20 | target = Target; 21 | } 22 | 23 | public override object Visit(Expression E) 24 | { 25 | target.AddRange(new SubstituteTransform(E, equal.Peek(), conditions)); 26 | return base.Visit(E); 27 | } 28 | 29 | protected override object VisitUnknown(Expression E) 30 | { 31 | return null; 32 | } 33 | 34 | protected override object VisitSum(Sum A) 35 | { 36 | foreach (Expression i in A.Terms) 37 | { 38 | equal.Push(equal.Peek() - i); 39 | Visit(Sum.New(A.Terms.ExceptUnique(i))); 40 | equal.Pop(); 41 | } 42 | return null; 43 | } 44 | 45 | protected override object VisitProduct(Product M) 46 | { 47 | foreach (Expression i in M.Terms) 48 | { 49 | conditions.Push(Binary.NotEqual(i, 0)); 50 | equal.Push(equal.Peek() / i); 51 | Visit(Product.New(M.Terms.ExceptUnique(i))); 52 | equal.Pop(); 53 | conditions.Pop(); 54 | } 55 | return null; 56 | } 57 | 58 | //protected override object VisitPower(Power P) 59 | //{ 60 | // equal.Push(Power.New(equal.Peek(), 1 / P.Right)); 61 | // Visit(P.Left); 62 | // equal.Pop(); 63 | // return null; 64 | //} 65 | } 66 | 67 | /// 68 | /// Transform that 69 | /// 70 | public class AlgebraTransform : TransformSet 71 | { 72 | protected void Init(Expression x, Expression y, IEnumerable PreConditions) 73 | { 74 | new AlgebraTransformGenerator(x, PreConditions, this).Visit(y); 75 | new AlgebraTransformGenerator(y, PreConditions, this).Visit(x); 76 | } 77 | 78 | /// 79 | /// Generate a set of transforms for the relationship x = y via basic algebraic inverses. 80 | /// For example, given transform Sin[x]/Cos[x] = Tan[x], also generates a transform Sin[x] = Tan[x]*Cos[x]. 81 | /// 82 | /// 83 | /// 84 | /// 85 | public AlgebraTransform(Expression x, Expression y, IEnumerable PreConditions) 86 | { 87 | Init(x, y, PreConditions); 88 | } 89 | public AlgebraTransform(Expression x, Expression y, params Expression[] PreConditions) 90 | { 91 | Init(x, y, PreConditions); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /ComputerAlgebra/Transform/CachedTransform.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// Cache the results of a transform. 7 | /// 8 | public class CachedTransform : ITransform 9 | { 10 | private Dictionary cache = new Dictionary(); 11 | private ITransform transform; 12 | 13 | /// 14 | /// 15 | /// 16 | /// Transform to cache the results of. 17 | public CachedTransform(ITransform T) { transform = T; } 18 | 19 | public Expression Transform(Expression E) 20 | { 21 | Expression TE; 22 | if (cache.TryGetValue(E, out TE)) 23 | return TE; 24 | TE = transform.Transform(E); 25 | cache[E] = TE; 26 | return TE; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ComputerAlgebra/Transform/LinearTransform.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Base class for a linear transform operation. 8 | /// 9 | public abstract class LinearTransform : VisitorTransform 10 | { 11 | /// 12 | /// Check if x is a constant for this transform. 13 | /// 14 | /// 15 | /// 16 | protected abstract bool IsConstant(Expression x); 17 | 18 | // V(x + y) = V(x) + V(y) 19 | protected override Expression VisitSum(Sum A) { return Sum.New(A.Terms.Select(i => Visit(i))); } 20 | 21 | // V(A*x) = A*V(x) 22 | protected override Expression VisitProduct(Product M) 23 | { 24 | IEnumerable A = M.Terms.Where(i => IsConstant(i)); 25 | if (A.Any()) 26 | return Product.New(A.Append(Visit(Product.New(M.Terms.Where(i => !IsConstant(i)))))); 27 | return base.VisitProduct(M); 28 | } 29 | 30 | // V((A*x)^n) = A^(1/n)*V(x^n) 31 | protected override Expression VisitPower(Power P) 32 | { 33 | if (!IsConstant(P.Right)) 34 | return base.VisitPower(P); 35 | 36 | Expression L = P.Left.Factor(); 37 | 38 | IEnumerable A = Product.TermsOf(L).Where(i => IsConstant(i)); 39 | if (A.Any()) 40 | return Product.New(Power.New(Product.New(A), 1 / P.Right), Visit(Product.New(Product.TermsOf(L).Where(i => !IsConstant(i))))); 41 | return base.VisitPower(P); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ComputerAlgebra/Transform/PatternTransform.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// Transform using a pattern expression. 8 | /// 9 | public abstract class PatternTransform : ITransform 10 | { 11 | protected Expression pattern; 12 | private IEnumerable conditions = null; 13 | 14 | protected abstract Expression ApplyTransform(Expression x, MatchContext Matched); 15 | 16 | protected PatternTransform(Expression Pattern, IEnumerable PreConditions) 17 | { 18 | pattern = Pattern; 19 | conditions = PreConditions.Buffer(); 20 | } 21 | 22 | /// 23 | /// Get the pattern for this transform. 24 | /// 25 | public Expression Pattern { get { return pattern; } } 26 | 27 | /// 28 | /// Transform an expression by matching to the pattern and substituting the result if successful. 29 | /// 30 | /// Expression to transform. 31 | /// The transformed expression. 32 | public Expression Transform(Expression x) 33 | { 34 | MatchContext matched = pattern.Matches(x); 35 | if (matched != null && conditions.All(i => i.Evaluate(matched).IsTrue())) 36 | return ApplyTransform(x, matched); 37 | else 38 | return x; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ComputerAlgebra/Transform/SubstituteTransform.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | /// 7 | /// PatternTransform that replaces the matched pattern with a result expression. 8 | /// 9 | public class SubstituteTransform : PatternTransform 10 | { 11 | private Expression result; 12 | 13 | protected override Expression ApplyTransform(Expression x, MatchContext Matched) 14 | { 15 | return result.Substitute(Matched, true).Evaluate(); 16 | } 17 | 18 | /// 19 | /// Construct a new PatternTransform. 20 | /// 21 | /// Pattern to match. 22 | /// Result to substitute. 23 | /// Conditions to check for using this transform. 24 | public SubstituteTransform(Expression Pattern, Expression Result, IEnumerable PreConditions) 25 | : base(Pattern, PreConditions) 26 | { 27 | result = Result; 28 | } 29 | 30 | /// 31 | /// Construct a new PatternTransform. 32 | /// 33 | /// Pattern to match. 34 | /// Result to substitute. 35 | /// Conditions to check for using this transform. 36 | public SubstituteTransform(Expression Pattern, Expression Result, params Expression[] PreConditions) : this(Pattern, Result, PreConditions.AsEnumerable()) { } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ComputerAlgebra/Transform/Transform.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | /// 4 | /// Interface for a Transform operation. 5 | /// 6 | public interface ITransform 7 | { 8 | /// 9 | /// Transform Expression E to a new Expression. 10 | /// 11 | /// 12 | /// 13 | Expression Transform(Expression E); 14 | } 15 | 16 | /// 17 | /// Transform operation utilizing ExpressionVisitor. 18 | /// 19 | public abstract class VisitorTransform : ExpressionVisitor, ITransform 20 | { 21 | protected override Expression VisitUnknown(Expression E) { return E; } 22 | 23 | public Expression Transform(Expression E) { return Visit(E); } 24 | } 25 | 26 | /// 27 | /// Transform operation utilizing RecursiveExpressionVisitor. 28 | /// 29 | public class RecursiveVisitorTransform : RecursiveExpressionVisitor, ITransform 30 | { 31 | public Expression Transform(Expression E) { return Visit(E); } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ComputerAlgebra/Transform/TransformSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | 7 | namespace ComputerAlgebra 8 | { 9 | /// 10 | /// Transform containing a set of pattern transforms. 11 | /// 12 | public class TransformSet : ITransform, IEnumerable 13 | { 14 | private static Expression ChildPattern(ref int unique) 15 | { 16 | unique++; 17 | return Variable.New("_" + unique.ToString()); 18 | } 19 | 20 | private static Expression Split(Expression P) 21 | { 22 | int child = 0; 23 | 24 | if (P is Sum) 25 | return ComputerAlgebra.Sum.New(((ComputerAlgebra.Sum)P).Terms.Select(i => ChildPattern(ref child))); 26 | if (P is Product) 27 | return Product.New(((Product)P).Terms.Select(i => ChildPattern(ref child))); 28 | if (P is Binary) 29 | return Binary.New(((Binary)P).Operator, ChildPattern(ref child), ChildPattern(ref child)); 30 | if (P is Unary) 31 | return Unary.New(((Unary)P).Operator, ChildPattern(ref child)); 32 | if (P is Call) 33 | return Call.New(((Call)P).Target, ((Call)P).Arguments.Select(i => ChildPattern(ref child))); 34 | return null; 35 | } 36 | 37 | private Expression pattern; 38 | private List children = new List(); 39 | private List transforms = new List(); 40 | 41 | private bool IsChild(Expression P) 42 | { 43 | return (pattern is null) || pattern.Matches(P) != null; 44 | } 45 | 46 | private TransformSet(Expression Pattern, PatternTransform Transform) { pattern = Pattern; Add(Transform); } 47 | 48 | public TransformSet(IEnumerable Transforms) { AddRange(Transforms); } 49 | public TransformSet(params PatternTransform[] Transforms) { AddRange(Transforms.AsEnumerable()); } 50 | 51 | /// 52 | /// Add a transform to the set. 53 | /// 54 | /// 55 | public void Add(PatternTransform T) 56 | { 57 | Debug.Assert(IsChild(T.Pattern)); 58 | 59 | // Try to add this transform to a child node. 60 | foreach (TransformSet i in children) 61 | { 62 | if (i.IsChild(T.Pattern)) 63 | { 64 | i.Add(T); 65 | return; 66 | } 67 | } 68 | 69 | // If the pattern can be split, create a new child. 70 | Expression parent = Split(T.Pattern); 71 | if (!(parent is null) && !parent.Equals(pattern)) 72 | { 73 | children.Add(new TransformSet(parent, T)); 74 | return; 75 | } 76 | 77 | // Can't add or create a child. Add the transform to this node. 78 | transforms.Add(T); 79 | } 80 | 81 | /// 82 | /// Add transforms to the set. 83 | /// 84 | /// 85 | public void AddRange(IEnumerable T) 86 | { 87 | foreach (PatternTransform i in T) 88 | Add(i); 89 | } 90 | public void AddRange(params PatternTransform[] T) { AddRange(T.AsEnumerable()); } 91 | 92 | public static int TransformCalls = 0; 93 | 94 | /// 95 | /// Transform expression with the first successful transform in the set. 96 | /// 97 | /// 98 | /// 99 | public Expression Transform(Expression x, Func Validate) 100 | { 101 | // If the expression doesn't match the base pattern, it won't match any of the transforms here. 102 | if (!(pattern is null) && pattern.Matches(x) == null) 103 | return x; 104 | 105 | // Try child nodes. 106 | foreach (TransformSet i in children) 107 | { 108 | Expression xi = i.Transform(x, Validate); 109 | if (!ReferenceEquals(xi, x)) 110 | return xi; 111 | } 112 | 113 | // Try transforms at this node. 114 | foreach (PatternTransform i in transforms) 115 | { 116 | ++TransformCalls; 117 | Expression xi = i.Transform(x); 118 | if (!ReferenceEquals(xi, x) && Validate(xi)) 119 | return xi; 120 | } 121 | 122 | return x; 123 | } 124 | 125 | /// 126 | /// Transform expression with the first successful transform in the set. 127 | /// 128 | /// 129 | /// 130 | public Expression Transform(Expression x) 131 | { 132 | return Transform(x, y => true); 133 | } 134 | 135 | public IEnumerator GetEnumerator() { return transforms.GetEnumerator(); } 136 | IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/BigFloat.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | ///// 4 | ///// Represents a floating point number with arbitrary length mantissa and exponent. 5 | ///// 6 | //public struct BigFloat : IComparable, IEquatable, IFormattable 7 | //{ 8 | // private BigInteger m, e; 9 | 10 | // public BigFloat(BigInteger n) : this(n, 0) { } 11 | // public BigFloat(BigInteger Mantissa, BigInteger Exponent) 12 | // { 13 | // m = Mantissa; 14 | // e = Exponent; 15 | 16 | // while ((m & 255) == 0) 17 | // { 18 | // m >>= 8; 19 | // e += 8; 20 | // } 21 | // while ((m & 1) == 0) 22 | // { 23 | // m >>= 1; 24 | // e += 1; 25 | // } 26 | // } 27 | 28 | // public static BigFloat operator -(BigFloat x) { return new BigFloat(-x.m, x.e); } 29 | // public static BigFloat operator +(BigFloat l, BigFloat r) 30 | // { 31 | // // Create a new BigFloat with the smaller exponent of l, r. 32 | // if (l.e > r.e) 33 | // return new BigFloat((l.m << (int)(l.e - r.e)) + r.m, r.e); 34 | // else 35 | // return new BigFloat((r.m << (int)(r.e - l.e)) + l.m, l.e); 36 | // } 37 | // public static BigFloat operator -(BigFloat l, BigFloat r) { return l + -r; } 38 | // public static BigFloat operator *(BigFloat l, BigFloat r) { return new BigFloat(l.m * r.m, l.e + r.e); } 39 | // public static BigFloat operator /(BigFloat l, BigFloat r) { return new BigFloat(l.m / r.m, l.e - r.e); } 40 | //} 41 | } 42 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/BigIntegerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | public static class BigIntegerExtensions 6 | { 7 | /// 8 | /// Returns the trailing number of 0 digits in x. 9 | /// 10 | /// 11 | /// 12 | /// 13 | public static int InsignificantDigits(this BigInteger x, int b) 14 | { 15 | BigInteger d = x; 16 | for (int i = 0; ; ++i) 17 | { 18 | BigInteger r; 19 | d = BigInteger.DivRem(d, b, out r); 20 | if (!r.IsZero) 21 | return i; 22 | } 23 | } 24 | 25 | public static int InsignificantDigits(this BigInteger x) { return InsignificantDigits(x, 10); } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/Combinatorics.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | public static class Combinatorics 7 | { 8 | /// 9 | /// Enumerate the permutations of x. 10 | /// 11 | /// 12 | /// 13 | /// 14 | public static IEnumerable> Permutations(this IEnumerable n) 15 | { 16 | List l = n.ToList(); 17 | return Permutations(l, l.Count); 18 | } 19 | 20 | private static IEnumerable> Permutations(IList n, int r) 21 | { 22 | if (r == 1) 23 | { 24 | yield return n; 25 | } 26 | else 27 | { 28 | for (int i = 0; i < r; i++) 29 | { 30 | foreach (var j in Permutations(n, r - 1)) 31 | yield return j; 32 | 33 | T t = n[r - 1]; 34 | n.RemoveAt(r - 1); 35 | n.Insert(0, t); 36 | } 37 | } 38 | } 39 | 40 | /// 41 | /// Enumerate the combinations of n of length r. 42 | /// From: http://www.extensionmethod.net/csharp/ienumerable-t/combinations 43 | /// 44 | /// 45 | /// 46 | /// 47 | /// 48 | public static IEnumerable> Combinations(this IEnumerable n, int r) 49 | { 50 | if (r == 0) 51 | return new[] { new T[0] }; 52 | else 53 | return n.SelectMany((e, i) => n.Skip(i + 1).Combinations(r - 1).Select(c => new[] { e }.Concat(c))); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/CustomAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace ComputerAlgebra 7 | { 8 | internal static class CustomAttributeExtension 9 | { 10 | public static IEnumerable CustomAttributes(this Type This, bool Inherit) where T : Attribute 11 | { 12 | return This.GetCustomAttributes(typeof(T), Inherit).Cast(); 13 | } 14 | 15 | public static IEnumerable CustomAttributes(this MemberInfo This, bool Inherit) where T : Attribute 16 | { 17 | return This.GetCustomAttributes(typeof(T), Inherit).Cast(); 18 | } 19 | 20 | public static IEnumerable CustomAttributes(this ParameterInfo This, bool Inherit) where T : Attribute 21 | { 22 | return This.GetCustomAttributes(typeof(T), Inherit).Cast(); 23 | } 24 | 25 | public static IEnumerable CustomAttributes(this Type This) where T : Attribute { return This.CustomAttributes(true); } 26 | public static IEnumerable CustomAttributes(this MemberInfo This) where T : Attribute { return This.CustomAttributes(true); } 27 | public static IEnumerable CustomAttributes(this ParameterInfo This) where T : Attribute { return This.CustomAttributes(true); } 28 | 29 | public static T CustomAttribute(this Type This, bool Inherit) where T : Attribute { return This.CustomAttributes(Inherit).FirstOrDefault(); } 30 | public static T CustomAttribute(this MemberInfo This, bool Inherit) where T : Attribute { return This.CustomAttributes(Inherit).FirstOrDefault(); } 31 | public static T CustomAttribute(this ParameterInfo This, bool Inherit) where T : Attribute { return This.CustomAttributes(Inherit).FirstOrDefault(); } 32 | 33 | public static T CustomAttribute(this Type This) where T : Attribute { return This.CustomAttribute(true); } 34 | public static T CustomAttribute(this MemberInfo This) where T : Attribute { return This.CustomAttribute(true); } 35 | public static T CustomAttribute(this ParameterInfo This) where T : Attribute { return This.CustomAttribute(true); } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/DefaultDictionary.cs: -------------------------------------------------------------------------------- 1 | namespace System.Collections.Generic 2 | { 3 | /// 4 | /// Dictionary implementing behavior similar to std::map for operator []. 5 | /// 6 | /// 7 | /// 8 | public class DefaultDictionary : Dictionary 9 | { 10 | private TValue def = default(TValue); 11 | /// 12 | /// The default value for new values. 13 | /// 14 | public TValue Default { get { return def; } set { def = value; } } 15 | 16 | public DefaultDictionary() { } 17 | public DefaultDictionary(TValue Default) { def = Default; } 18 | public DefaultDictionary(IDictionary Copy) : base(Copy) { } 19 | public DefaultDictionary(IDictionary Copy, TValue Default) : base(Copy) { def = Default; } 20 | 21 | /// 22 | /// The same as Dictionary's indexer, except if the key does not exist, return Default. 23 | /// 24 | /// 25 | /// 26 | public new TValue this[TKey K] 27 | { 28 | get 29 | { 30 | TValue V; 31 | if (base.TryGetValue(K, out V)) 32 | return V; 33 | else 34 | return Default; 35 | } 36 | set 37 | { 38 | if (Equals(Default, value)) 39 | base.Remove(K); 40 | else 41 | base[K] = value; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace System.Collections.Generic 4 | { 5 | public static class DictionaryExtensions 6 | { 7 | /// 8 | /// Remove all dictionary elements matching the given predicate. 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | public static void RemoveAll(this IDictionary This, 15 | Func, bool> Predicate) 16 | { 17 | var Keys = This.Where(i => Predicate(i)).ToList(); 18 | foreach (var i in Keys) 19 | This.Remove(i.Key); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/ExpressionConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | class ExpressionConverter : TypeConverter 7 | { 8 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 9 | { 10 | return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType); 11 | } 12 | public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) 13 | { 14 | if (value is string) 15 | return Expression.Parse((string)value); 16 | return base.ConvertFrom(context, culture, value); 17 | } 18 | public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) 19 | { 20 | if (destinationType == typeof(string)) 21 | return ((Expression)value).ToString(); 22 | return base.ConvertTo(context, culture, value, destinationType); 23 | } 24 | public override bool IsValid(ITypeDescriptorContext context, object value) 25 | { 26 | if (value is string) 27 | { 28 | try 29 | { 30 | Expression.Parse((string)value); 31 | return true; 32 | } 33 | catch (Exception) 34 | { 35 | return false; 36 | } 37 | } 38 | 39 | return base.IsValid(context, value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/LazyExpression.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerAlgebra 2 | { 3 | /// 4 | /// Expression that is evaluated upon conversion to Expression. 5 | /// 6 | public class LazyExpression 7 | { 8 | private Expression value; 9 | private Expression evaluated = null; 10 | 11 | /// 12 | /// Construct a new lazy expression. 13 | /// 14 | /// Expression to be lazily evaluated. 15 | public LazyExpression(Expression Expr) { value = Expr; } 16 | 17 | /// 18 | /// Implicit conversion from LazyExpression to Expression. 19 | /// 20 | /// 21 | /// Evaluated value of x. 22 | public static implicit operator Expression(LazyExpression x) 23 | { 24 | if (ReferenceEquals(x.evaluated, null)) 25 | x.evaluated = x.value.Evaluate(); 26 | 27 | return x.evaluated; 28 | } 29 | 30 | public static implicit operator LazyExpression(Real x) { return new LazyExpression(x); } 31 | public static implicit operator LazyExpression(BigRational x) { return new LazyExpression(x); } 32 | public static implicit operator LazyExpression(decimal x) { return new LazyExpression(x); } 33 | public static implicit operator LazyExpression(double x) { return new LazyExpression(x); } 34 | public static implicit operator LazyExpression(int x) { return new LazyExpression(x); } 35 | public static implicit operator LazyExpression(string x) { return new LazyExpression(x); } 36 | 37 | public static implicit operator bool(LazyExpression x) { return (Expression)x; } 38 | public static explicit operator Real(LazyExpression x) { return (Real)(Expression)x; } 39 | public static explicit operator double(LazyExpression x) { return (double)(Expression)x; } 40 | 41 | public static bool operator true(LazyExpression x) { return ((Expression)x).IsTrue(); } 42 | public static bool operator false(LazyExpression x) { return ((Expression)x).IsFalse(); } 43 | 44 | // Expression operators. 45 | public static LazyExpression operator +(LazyExpression L, LazyExpression R) { return new LazyExpression(Sum.New(L.value, R.value)); } 46 | public static LazyExpression operator -(LazyExpression L, LazyExpression R) { return new LazyExpression(Sum.New(L.value, Unary.Negate(R.value))); } 47 | public static LazyExpression operator *(LazyExpression L, LazyExpression R) { return new LazyExpression(Product.New(L.value, R.value)); } 48 | public static LazyExpression operator /(LazyExpression L, LazyExpression R) { return new LazyExpression(Product.New(L.value, Power.New(R.value, -1))); } 49 | public static LazyExpression operator ^(LazyExpression L, LazyExpression R) { return new LazyExpression(Binary.Power(L.value, R.value)); } 50 | public static LazyExpression operator -(LazyExpression O) { return new LazyExpression(Product.New(-1, O.value)); } 51 | 52 | public static LazyExpression operator &(LazyExpression L, LazyExpression R) { return new LazyExpression(Binary.And(L.value, R.value)); } 53 | public static LazyExpression operator |(LazyExpression L, LazyExpression R) { return new LazyExpression(Binary.Or(L.value, R.value)); } 54 | public static LazyExpression operator !(LazyExpression O) { return new LazyExpression(Unary.Not(O.value)); } 55 | 56 | public static LazyExpression operator ==(LazyExpression L, LazyExpression R) 57 | { 58 | if (ReferenceEquals(L.value, null) || ReferenceEquals(R.value, null)) 59 | return new LazyExpression(Constant.New(ReferenceEquals(L.value, R.value))); 60 | return new LazyExpression(Binary.Equal(L.value, R.value)); 61 | } 62 | public static LazyExpression operator !=(LazyExpression L, LazyExpression R) 63 | { 64 | if (ReferenceEquals(L.value, null) || ReferenceEquals(R.value, null)) 65 | return new LazyExpression(Constant.New(!ReferenceEquals(L.value, R.value))); 66 | return new LazyExpression(Binary.NotEqual(L.value, R.value)); 67 | } 68 | public static LazyExpression operator <(LazyExpression L, LazyExpression R) { return new LazyExpression(Binary.Less(L.value, R.value)); } 69 | public static LazyExpression operator <=(LazyExpression L, LazyExpression R) { return new LazyExpression(Binary.LessEqual(L.value, R.value)); } 70 | public static LazyExpression operator >(LazyExpression L, LazyExpression R) { return new LazyExpression(Binary.Greater(L.value, R.value)); } 71 | public static LazyExpression operator >=(LazyExpression L, LazyExpression R) { return new LazyExpression(Binary.GreaterEqual(L.value, R.value)); } 72 | 73 | // object interface. 74 | public override int GetHashCode() { return ((Expression)this).GetHashCode(); } 75 | public override bool Equals(object obj) { return ((Expression)this).Equals(obj); } 76 | public override string ToString() { return ((Expression)this).ToString(); } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Collections.Generic 2 | { 3 | /// 4 | /// Extensions for List. 5 | /// 6 | static class ListExtensions 7 | { 8 | private class SortComparer : IComparer where TKey : IComparable 9 | { 10 | private Func selector; 11 | 12 | public SortComparer(Func Selector) { selector = Selector; } 13 | 14 | public int Compare(T L, T R) { return selector(L).CompareTo(selector(R)); } 15 | } 16 | 17 | /// 18 | /// Sort the list using a selector, as in IEnumeable.OrderBy. 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | public static void Sort(this List This, Func Selector) where TKey : IComparable 25 | { 26 | This.Sort(new SortComparer(Selector)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/MatchContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ComputerAlgebra 6 | { 7 | /// 8 | /// Store matched variables with the ability to revert to a previous match state upon matching failure. 9 | /// 10 | public class MatchContext : Dictionary 11 | { 12 | private List history = new List(); 13 | 14 | private Expression matched; 15 | public Expression Matched { get { return matched; } } 16 | 17 | /// 18 | /// Create a match context with conditions. 19 | /// 20 | /// 21 | public MatchContext(Expression Matching, IEnumerable PreMatch) 22 | { 23 | matched = Matching; 24 | foreach (Arrow i in PreMatch) 25 | if (!Matches(i.Left, i.Right)) 26 | throw new InvalidOperationException("Duplicate prematch failed."); 27 | } 28 | public MatchContext(Expression Matching, params Arrow[] PreMatch) : this(Matching, PreMatch.AsEnumerable()) { } 29 | 30 | /// 31 | /// Check if Key has already been matched to Value. If not, store it as the match. 32 | /// 33 | /// 34 | /// 35 | /// false if Key was already matched to an expression other than Value, true otherwise. 36 | public bool Matches(Expression Key, Expression Value) 37 | { 38 | Expression Matched; 39 | if (TryGetValue(Key, out Matched)) 40 | return Matched.Equals(Value); 41 | 42 | this[Key] = Value; 43 | history.Add(Key); 44 | return true; 45 | } 46 | 47 | /// 48 | /// Store the state of the matching context and attempt to execute a function F. If the function returns false, the saved state is restored. 49 | /// 50 | /// The function to execute. 51 | /// The result of F. 52 | public bool TryMatch(Func F) 53 | { 54 | // Remember where this context begins. 55 | int at = history.Count; 56 | if (F()) 57 | return true; 58 | 59 | // Match failed, remove any matches since the beginning of this context. 60 | for (int i = at; i < history.Count; ++i) 61 | Remove(history[i]); 62 | history.RemoveRange(at, history.Count - at); 63 | return false; 64 | } 65 | 66 | public override string ToString() 67 | { 68 | return String.Join(", ", this.Select(i => i.Key.ToString() + " -> " + i.Value.ToString())); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/RealConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace ComputerAlgebra 5 | { 6 | class RealConverter : TypeConverter 7 | { 8 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 9 | { 10 | return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType); 11 | } 12 | public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) 13 | { 14 | if (value is string) 15 | return Real.Parse((string)value); 16 | return base.ConvertFrom(context, culture, value); 17 | } 18 | public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) 19 | { 20 | if (destinationType == typeof(string)) 21 | return ((Real)value).ToString(); 22 | return base.ConvertTo(context, culture, value, destinationType); 23 | } 24 | public override bool IsValid(ITypeDescriptorContext context, object value) 25 | { 26 | if (value is string) 27 | { 28 | try 29 | { 30 | Real.Parse((string)value); 31 | return true; 32 | } 33 | catch (Exception) 34 | { 35 | return false; 36 | } 37 | } 38 | 39 | return base.IsValid(context, value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/ReferenceEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ComputerAlgebra 4 | { 5 | /// 6 | /// IEqualityComparer implementing reference equality. 7 | /// 8 | /// 9 | public class ReferenceEqualityComparer : EqualityComparer 10 | { 11 | public override bool Equals(T a, T b) { return ReferenceEquals(a, b); } 12 | public override int GetHashCode(T obj) { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ComputerAlgebra/Utils/StringVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace ComputerAlgebra 7 | { 8 | class StringVisitor : ExpressionVisitor 9 | { 10 | string numberFormat; 11 | IFormatProvider numberFormatProvider; 12 | 13 | public string NumberFormat { get { return numberFormat; } set { numberFormat = value; } } 14 | public IFormatProvider NumberFormatProvider { get { return numberFormatProvider; } set { numberFormatProvider = value; } } 15 | 16 | protected static bool IsNegative(Expression x) 17 | { 18 | Constant C = Product.TermsOf(x).FirstOrDefault(i => i is Constant) as Constant; 19 | if (C != null) 20 | return (Real)C < 0; 21 | return false; 22 | } 23 | 24 | protected string Join(IEnumerable x, string Delim) 25 | { 26 | if (x.Any()) 27 | { 28 | string unsplit = Visit(x.First()); 29 | foreach (Expression i in x.Skip(1)) 30 | unsplit = unsplit + Delim + Visit(i); 31 | return unsplit; 32 | } 33 | return ""; 34 | } 35 | 36 | protected string Visit(Expression E, int Precedence) 37 | { 38 | string v = Visit(E); 39 | if (Parser.Precedence(E) < Precedence) 40 | v = "(" + v + ")"; 41 | return v; 42 | } 43 | 44 | protected override string VisitProduct(Product P) 45 | { 46 | int pr = Parser.Precedence(Operator.Multiply); 47 | 48 | int sign = 1; 49 | StringBuilder s = new StringBuilder(); 50 | foreach (Expression i in P.Terms) 51 | { 52 | if (i.Equals(-1)) 53 | { 54 | sign *= -1; 55 | } 56 | else 57 | { 58 | if (s.Length > 0) 59 | s.Append('*'); 60 | s.Append(Visit(i, pr)); 61 | } 62 | } 63 | if (sign == -1) 64 | return "-" + s.ToString(); 65 | else 66 | return s.ToString(); 67 | } 68 | 69 | protected override string VisitSum(Sum S) 70 | { 71 | int pr = Parser.Precedence(Operator.Add); 72 | 73 | StringBuilder s = new StringBuilder(); 74 | s.Append(Visit(S.Terms.First())); 75 | foreach (Expression i in S.Terms.Skip(1)) 76 | { 77 | string si = Visit(i, pr); 78 | string nsi = Visit(-i, pr); 79 | if (si.Length < nsi.Length) 80 | s.Append(" + " + si); 81 | else 82 | s.Append(" - " + nsi); 83 | } 84 | return s.ToString(); 85 | } 86 | 87 | protected override string VisitMatrix(Matrix m) 88 | { 89 | StringBuilder s = new StringBuilder(); 90 | 91 | s.Append("["); 92 | for (int i = 0; i < m.M; ++i) 93 | { 94 | s.Append("["); 95 | for (int j = 0; j < m.N; ++j) 96 | { 97 | if (j > 0) s.Append(", "); 98 | s.Append(m[i, j].ToString()); 99 | } 100 | s.Append("]"); 101 | } 102 | s.Append("]"); 103 | 104 | return s.ToString(); 105 | } 106 | 107 | protected override string VisitBinary(Binary B) 108 | { 109 | int pr = Parser.Precedence(B.Operator); 110 | return Visit(B.Left, pr) 111 | + Binary.ToString(B.Operator) 112 | + Visit(B.Right, pr); 113 | } 114 | 115 | protected override string VisitSet(Set S) 116 | { 117 | IEnumerable members = S.Members; 118 | // If there are more than 100 elements in the set, truncate all but 5 elements and append '...'. 119 | if (members.Skip(100).Any()) 120 | members = members.Take(5).Append(Variable.New("...")); 121 | 122 | string s = "{" + Join(members, ", ") + "}"; 123 | return s; 124 | } 125 | 126 | protected override string VisitUnary(Unary U) 127 | { 128 | int pr = Parser.Precedence(U.Operator); 129 | return Unary.ToStringPrefix(U.Operator) 130 | + Visit(U.Operand, pr) 131 | + Unary.ToStringPostfix(U.Operator); 132 | } 133 | 134 | protected override string VisitCall(Call F) 135 | { 136 | string s = F.Target.Name + "[" + Join(F.Arguments, ", ") + "]"; 137 | return s; 138 | } 139 | 140 | protected override string VisitVariable(Variable V) 141 | { 142 | return V.Name; 143 | } 144 | 145 | protected override string VisitConstant(Constant C) 146 | { 147 | return C.Value.ToString(NumberFormat, NumberFormatProvider); 148 | } 149 | 150 | public override string Visit(Expression E) 151 | { 152 | if (E is Function) 153 | return E.GetType().ToString(); 154 | return base.Visit(E); 155 | } 156 | 157 | protected override string VisitUnknown(Expression E) 158 | { 159 | throw new NotImplementedException("ToString(" + E.GetType().ToString() + ")"); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Console/Console.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0-windows 4 | Exe 5 | Consile 6 | Console 7 | Copyright © 2020 8 | 1.0.0.0 9 | 1.0.0.0 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Console/Program.cs: -------------------------------------------------------------------------------- 1 | using ComputerAlgebra; 2 | using ComputerAlgebra.LinqCompiler; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Console 7 | { 8 | class Program 9 | { 10 | public static void Plot(Expression f, Variable x, Constant x0, Constant x1) 11 | { 12 | ComputerAlgebra.Plotting.Plot p = new ComputerAlgebra.Plotting.Plot() 13 | { 14 | x0 = (double)x0, 15 | x1 = (double)x1, 16 | Title = f.ToString(), 17 | xLabel = x.ToString(), 18 | }; 19 | foreach (Expression i in Set.MembersOf(f)) 20 | p.Series.Add(new ComputerAlgebra.Plotting.Function(ExprFunction.New(i, x).Compile>()) { Name = i.ToString() }); 21 | } 22 | 23 | static void Main(string[] args) 24 | { 25 | List> InOut = new List>(); 26 | 27 | DynamicNamespace console = new DynamicNamespace(); 28 | 29 | console.Add(NativeFunction.New>("Plot", (f, x, x0, x1) => Plot(f, x, x0, x1))); 30 | console.Add(NativeFunction.New>("Plot", (f, x) => Plot(f, x, -10, 10))); 31 | console.Add(NativeFunction.New("Clear", () => System.Console.Clear())); 32 | console.Add(NativeFunction.New>("In", x => InOut[(int)x].Key)); 33 | console.Add(NativeFunction.New>("Out", x => InOut[(int)x].Value)); 34 | 35 | Parser parser = new Parser(new NamespaceSet(Namespace.Global, console)); 36 | 37 | while (true) 38 | { 39 | try 40 | { 41 | System.Console.Write("> "); 42 | string s = System.Console.ReadLine(); 43 | System.Console.WriteLine(); 44 | 45 | if (s == "Exit") 46 | break; 47 | 48 | Expression input = parser.Parse(s); 49 | Expression output = input.Evaluate(); 50 | 51 | int n = InOut.Count; 52 | 53 | InOut.Add(new KeyValuePair(input, output)); 54 | 55 | System.Console.WriteLine(Arrow.New("In[" + n + "]", input).ToPrettyString()); 56 | System.Console.WriteLine(); 57 | System.Console.WriteLine(Arrow.New("Out[" + n + "]", output).ToPrettyString()); 58 | System.Console.WriteLine(); 59 | } 60 | catch (Exception Ex) 61 | { 62 | System.Console.WriteLine(Ex.Message); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Demo/Intro/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Demo/Intro/Intro.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net60-windows 4 | Exe 5 | Intro 6 | Intro 7 | Copyright © 2020 8 | 1.0.0.0 9 | 1.0.0.0 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Demo/Intro/Program.cs: -------------------------------------------------------------------------------- 1 | using ComputerAlgebra; 2 | using ComputerAlgebra.LinqCompiler; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Intro 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | // Create some constants. 13 | Expression A = 2; 14 | Constant B = Constant.New(3); 15 | 16 | // Create some variables. 17 | Expression x = "x"; 18 | Variable y = Variable.New("y"); 19 | 20 | // Create a basic expressions. 21 | Expression f = A * x + B * y + 4; 22 | 23 | // This expression uses the implicit conversion from string to 24 | // Expression, which parses the string. 25 | Expression g = "5*x + C*y + 8"; 26 | 27 | // Create a system of equations from the above expressions. 28 | var system = new List() 29 | { 30 | Equal.New(f, 0), 31 | Equal.New(g, 0), 32 | }; 33 | 34 | // We can now solve the system of equations for x and y. Since the 35 | // equations have a variable 'C', the solutions will not be 36 | // constants. 37 | List solutions = system.Solve(x, y); 38 | Console.WriteLine("The solutions are:"); 39 | foreach (Arrow i in solutions) 40 | Console.WriteLine(i.ToString()); 41 | 42 | // A fundamental building block of ComputerAlgebra is the 'Arrow' 43 | // expression. Arrow expressions define the value of one expression 44 | // to be the value given by another expression. For example, 'x->2' 45 | // defines the expression 'x' to have the value '2'. 46 | 47 | // The 'Solve' function used above returns a list of Arrow 48 | // expressions defining the solutions of the system. 49 | 50 | // Arrow expressions are used by the 'Evaluate' function to 51 | // substitute values for expressions into other expressions. To 52 | // demonstrate the usage of Evaluate, let's validate the solution 53 | // by using Evaluate to substitute the solutions into the original 54 | // system of equations, and then substitute a value for C. 55 | Expression f_xy = f.Evaluate(solutions).Evaluate(Arrow.New("C", 2)); 56 | Expression g_xy = g.Evaluate(solutions).Evaluate(Arrow.New("C", 2)); 57 | if ((f_xy == 0) && (g_xy == 0)) 58 | Console.WriteLine("Success!"); 59 | else 60 | Console.WriteLine("Failure! f = {0}, g = {1}", f_xy, g_xy); 61 | 62 | // Suppose we need to evaluate the solutions efficiently many times. 63 | // We can compile the solutions to delegates where 'C' is a 64 | // parameter to the delegate, allowing it to be specified later. 65 | var x_C = x.Evaluate(solutions).Compile>("C"); 66 | var y_C = y.Evaluate(solutions).Compile>("C"); 67 | 68 | for (int i = 0; i < 20; ++i) 69 | { 70 | double C = i / 2.0; 71 | Console.WriteLine("C = {0}: (x, y) = ({1}, {2})", C, x_C(C), y_C(C)); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Demo/LotkaVolterra/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Demo/LotkaVolterra/LotkaVolterra.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net60-windows 4 | Exe 5 | LotkaVolterra 6 | LotkaVolterra 7 | Copyright © 2020 8 | 1.0.0.0 9 | 1.0.0.0 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Demo/LotkaVolterraCpp/LotkaVolterraCpp.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {6C0EAE2E-B231-47DF-81B4-D6152712408A} 15 | Win32Proj 16 | LotkaVolterraCpp 17 | 10.0 18 | 19 | 20 | 21 | Application 22 | true 23 | v143 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v143 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | true 45 | $(ProjectDir)$(Configuration)\ 46 | 47 | 48 | false 49 | $(ProjectDir)$(Configuration)\ 50 | 51 | 52 | 53 | 54 | 55 | Level3 56 | Disabled 57 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 58 | true 59 | 60 | 61 | Console 62 | true 63 | 64 | 65 | 66 | 67 | Level3 68 | 69 | 70 | MaxSpeed 71 | true 72 | true 73 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 74 | true 75 | 76 | 77 | Console 78 | true 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Demo/LotkaVolterraCpp/LotkaVolterraCpp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /Demo/LotkaVolterraCpp/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // This program is a companion to the LotkaVolterra project. It provides a C++ implementation 5 | // of the SimulateNativeHardCoded test case. 6 | 7 | // The same as SimulateNative from LotkaVolterra, but hardcoded in C++. 8 | void SimulateNativeHardCoded(int N, double* x) 9 | { 10 | // Loop over the sample range requested. 11 | for (int n = 1; n < N; ++n) 12 | { 13 | double x0 = x[0]; 14 | double x1 = x[1]; 15 | double x2 = x[2]; 16 | double x3 = x[3]; 17 | 18 | double dx0_dt = 1.0 19 | - 1 * x0 20 | - 1.09 * x1 21 | - 1.52 * x2 22 | - 0 * x3; 23 | double dx1_dt = 1.0 24 | - 0 * x0 25 | - 1 * x1 26 | - 0.44 * x2 27 | - 1.36 * x3; 28 | double dx2_dt = 1.0 29 | - 2.33 * x0 30 | - 0 * x1 31 | - 1 * x2 32 | - 0.47 * x3; 33 | double dx3_dt = 1.0 34 | - 1.21 * x0 35 | - 0.51 * x1 36 | - 0.35 * x2 37 | - 1 * x3; 38 | 39 | dx0_dt *= 0.001 * 1 * x0; 40 | dx1_dt *= 0.001 * 0.72 * x1; 41 | dx2_dt *= 0.001 * 1.53 * x2; 42 | dx3_dt *= 0.001 * 1.27 * x3; 43 | 44 | x += 4; 45 | 46 | x[0] = x0 + dx0_dt; 47 | x[1] = x1 + dx1_dt; 48 | x[2] = x2 + dx2_dt; 49 | x[3] = x3 + dx3_dt; 50 | } 51 | } 52 | 53 | int main(int argc, char ** argv) 54 | { 55 | const int N = 100000; 56 | 57 | // Array of doubles representing the populations over time. 58 | double* data = new double[N * 4]; 59 | // Initialize the system to have the population specified in the system. 60 | data[0] = 0.2; 61 | data[1] = 0.4586; 62 | data[2] = 0.1307; 63 | data[3] = 0.3557; 64 | 65 | // Start time of the simulation. 66 | __int64 start; 67 | QueryPerformanceCounter((LARGE_INTEGER *)&start); 68 | // run the simulation 100 times to avoid noise. 69 | for (int i = 0; i < 100; ++i) 70 | SimulateNativeHardCoded(N, data); 71 | 72 | std::cout << data[(N - 1) * 4 + 3] << std::endl; 73 | 74 | __int64 end; 75 | QueryPerformanceCounter((LARGE_INTEGER *)&end); 76 | __int64 freq; 77 | QueryPerformanceFrequency((LARGE_INTEGER *)&freq); 78 | std::cout << "SimulateNativeHardCoded time: " << (double)(end - start) / (double)freq << std::endl; 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Dillon Sharlet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0-windows 4 | Exe 5 | Tests 6 | Tests 7 | Copyright © 2020 8 | 1.0.0.0 9 | 1.0.0.0 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | --------------------------------------------------------------------------------