From abc4cdf18b57c29c401e10db4ae9420778ee10fc Mon Sep 17 00:00:00 2001 From: "Simon.Coghlan" Date: Fri, 13 Sep 2019 14:04:32 +0100 Subject: [PATCH 1/4] - Convert to .NetCore2.2; Net4; Net46; Net47 - Fix declarations of overridden API's - Remove WinTellect - - Use Tuple instead - Remove PowerColoctions - - Use SortedList - Fix code to still work in Net35 --- MathMLToCSharp.sln | 20 +- MathMLToCSharp/MathMLToCSharp.csproj | 2 +- MathMLToCSharpLib.o/BuildContext.cs | 192 +++++++++++++++ MathMLToCSharpLib.o/BuildContextOptions.cs | 210 +++++++++++++++++ MathMLToCSharpLib.o/Conversion.cs | 33 +++ MathMLToCSharpLib.o/Entities/Annotation.cs | 16 ++ .../Entities/BuildablePlainSum.cs | 64 +++++ MathMLToCSharpLib.o/Entities/IBuildable.cs | 20 ++ MathMLToCSharpLib.o/Entities/ISum.cs | 7 + MathMLToCSharpLib.o/Entities/Math.cs | 107 +++++++++ MathMLToCSharpLib.o/Entities/Mfenced.cs | 25 ++ MathMLToCSharpLib.o/Entities/Mfrac.cs | 67 ++++++ MathMLToCSharpLib.o/Entities/Mi.cs | 109 +++++++++ MathMLToCSharpLib.o/Entities/Mn.cs | 50 ++++ MathMLToCSharpLib.o/Entities/Mo.cs | 181 +++++++++++++++ MathMLToCSharpLib.o/Entities/Mroot.cs | 22 ++ MathMLToCSharpLib.o/Entities/Mrow.cs | 67 ++++++ MathMLToCSharpLib.o/Entities/Msqrt.cs | 22 ++ MathMLToCSharpLib.o/Entities/Mstyle.cs | 9 + MathMLToCSharpLib.o/Entities/Msub.cs | 36 +++ MathMLToCSharpLib.o/Entities/Msubsup.cs | 61 +++++ MathMLToCSharpLib.o/Entities/Msup.cs | 99 ++++++++ MathMLToCSharpLib.o/Entities/Mtable.cs | 33 +++ MathMLToCSharpLib.o/Entities/Mtd.cs | 18 ++ MathMLToCSharpLib.o/Entities/Mtr.cs | 25 ++ MathMLToCSharpLib.o/Entities/Munderover.cs | 30 +++ MathMLToCSharpLib.o/Entities/Null.cs | 9 + MathMLToCSharpLib.o/Entities/Semantics.cs | 115 +++++++++ .../Entities/WithBinaryContent.cs | 78 +++++++ .../Entities/WithBuildableContent.cs | 65 ++++++ .../Entities/WithBuildableContents.cs | 219 ++++++++++++++++++ .../Entities/WithTextContent.cs | 68 ++++++ MathMLToCSharpLib.o/MathMLToCSharpLib.csproj | 81 +++++++ MathMLToCSharpLib.o/MathMLToCSharpLib.nuspec | 16 ++ MathMLToCSharpLib.o/Parser.cs | 76 ++++++ .../PowerCollections.dll | Bin .../Properties/AssemblyInfo.cs | 0 MathMLToCSharpLib.o/Singleton.cs | 24 ++ MathMLToCSharpLib.o/Util.cs | 101 ++++++++ MathMLToCSharpLib/BuildContext.cs | 34 +-- MathMLToCSharpLib/Entities/Math.cs | 8 +- MathMLToCSharpLib/Entities/Mi.cs | 11 +- MathMLToCSharpLib/Entities/Mo.cs | 11 +- MathMLToCSharpLib/Entities/Msup.cs | 6 +- MathMLToCSharpLib/Entities/Semantics.cs | 126 +++++----- .../Entities/WithBinaryContent.cs | 5 +- .../Entities/WithBuildableContents.cs | 14 +- MathMLToCSharpLib/MathMLToCSharpLib.csproj | 98 ++------ MathMLToCSharpLib/MathMLToCSharpLib.nuspec | 2 +- MathMLToCSharpLib/PreNet40.cs | 121 ++++++++++ Tests/ConversionTests.cs | 114 ++++----- Tests/Tests.csproj | 6 +- 52 files changed, 2675 insertions(+), 258 deletions(-) create mode 100644 MathMLToCSharpLib.o/BuildContext.cs create mode 100644 MathMLToCSharpLib.o/BuildContextOptions.cs create mode 100644 MathMLToCSharpLib.o/Conversion.cs create mode 100644 MathMLToCSharpLib.o/Entities/Annotation.cs create mode 100644 MathMLToCSharpLib.o/Entities/BuildablePlainSum.cs create mode 100644 MathMLToCSharpLib.o/Entities/IBuildable.cs create mode 100644 MathMLToCSharpLib.o/Entities/ISum.cs create mode 100644 MathMLToCSharpLib.o/Entities/Math.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mfenced.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mfrac.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mi.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mn.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mo.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mroot.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mrow.cs create mode 100644 MathMLToCSharpLib.o/Entities/Msqrt.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mstyle.cs create mode 100644 MathMLToCSharpLib.o/Entities/Msub.cs create mode 100644 MathMLToCSharpLib.o/Entities/Msubsup.cs create mode 100644 MathMLToCSharpLib.o/Entities/Msup.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mtable.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mtd.cs create mode 100644 MathMLToCSharpLib.o/Entities/Mtr.cs create mode 100644 MathMLToCSharpLib.o/Entities/Munderover.cs create mode 100644 MathMLToCSharpLib.o/Entities/Null.cs create mode 100644 MathMLToCSharpLib.o/Entities/Semantics.cs create mode 100644 MathMLToCSharpLib.o/Entities/WithBinaryContent.cs create mode 100644 MathMLToCSharpLib.o/Entities/WithBuildableContent.cs create mode 100644 MathMLToCSharpLib.o/Entities/WithBuildableContents.cs create mode 100644 MathMLToCSharpLib.o/Entities/WithTextContent.cs create mode 100644 MathMLToCSharpLib.o/MathMLToCSharpLib.csproj create mode 100644 MathMLToCSharpLib.o/MathMLToCSharpLib.nuspec create mode 100644 MathMLToCSharpLib.o/Parser.cs rename {MathMLToCSharpLib => MathMLToCSharpLib.o}/PowerCollections.dll (100%) rename {MathMLToCSharpLib => MathMLToCSharpLib.o}/Properties/AssemblyInfo.cs (100%) create mode 100644 MathMLToCSharpLib.o/Singleton.cs create mode 100644 MathMLToCSharpLib.o/Util.cs create mode 100644 MathMLToCSharpLib/PreNet40.cs diff --git a/MathMLToCSharp.sln b/MathMLToCSharp.sln index 7a037fe..e021f35 100644 --- a/MathMLToCSharp.sln +++ b/MathMLToCSharp.sln @@ -1,18 +1,18 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29306.81 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Article", "Article", "{01843432-5BFE-490C-8C0F-196BB3461020}" ProjectSection(SolutionItems) = preProject - .\Article\MmlSharp.htm = .\Article\MmlSharp.htm + Article\MmlSharp.htm = Article\MmlSharp.htm EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathMLToCSharp", ".\MathMLToCSharp\MathMLToCSharp.csproj", "{9C1737F0-6DCC-4CC5-8C8E-F9B4073683F8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathMLToCSharpLib", "MathMLToCSharpLib\MathMLToCSharpLib.csproj", "{7E67AAFD-564C-4362-B93F-7FDB912E2DF7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", ".\Tests\Tests.csproj", "{320B2668-663F-49D1-9ADC-60D7674D1BAC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathMLToCSharp", "MathMLToCSharp\MathMLToCSharp.csproj", "{9C1737F0-6DCC-4CC5-8C8E-F9B4073683F8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathMLToCSharpLib", ".\MathMLToCSharpLib\MathMLToCSharpLib.csproj", "{3909E9A8-4E8A-4358-A8A9-3BA634090378}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{320B2668-663F-49D1-9ADC-60D7674D1BAC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -20,6 +20,10 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7E67AAFD-564C-4362-B93F-7FDB912E2DF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E67AAFD-564C-4362-B93F-7FDB912E2DF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E67AAFD-564C-4362-B93F-7FDB912E2DF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E67AAFD-564C-4362-B93F-7FDB912E2DF7}.Release|Any CPU.Build.0 = Release|Any CPU {9C1737F0-6DCC-4CC5-8C8E-F9B4073683F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9C1737F0-6DCC-4CC5-8C8E-F9B4073683F8}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C1737F0-6DCC-4CC5-8C8E-F9B4073683F8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -28,10 +32,6 @@ Global {320B2668-663F-49D1-9ADC-60D7674D1BAC}.Debug|Any CPU.Build.0 = Debug|Any CPU {320B2668-663F-49D1-9ADC-60D7674D1BAC}.Release|Any CPU.ActiveCfg = Release|Any CPU {320B2668-663F-49D1-9ADC-60D7674D1BAC}.Release|Any CPU.Build.0 = Release|Any CPU - {3909E9A8-4E8A-4358-A8A9-3BA634090378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3909E9A8-4E8A-4358-A8A9-3BA634090378}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3909E9A8-4E8A-4358-A8A9-3BA634090378}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3909E9A8-4E8A-4358-A8A9-3BA634090378}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MathMLToCSharp/MathMLToCSharp.csproj b/MathMLToCSharp/MathMLToCSharp.csproj index 2552bf1..37c2ce4 100644 --- a/MathMLToCSharp/MathMLToCSharp.csproj +++ b/MathMLToCSharp/MathMLToCSharp.csproj @@ -44,7 +44,7 @@ - packages\MathNet.Numerics.3.20.0\lib\net35\MathNet.Numerics.dll + ..\packages\MathNet.Numerics.3.20.0\lib\net35\MathNet.Numerics.dll False diff --git a/MathMLToCSharpLib.o/BuildContext.cs b/MathMLToCSharpLib.o/BuildContext.cs new file mode 100644 index 0000000..f7bbf29 --- /dev/null +++ b/MathMLToCSharpLib.o/BuildContext.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +using MathMLToCSharpLib.Entities; + +using Wintellect.PowerCollections; + +namespace MathMLToCSharpLib +{ + /// + /// Represents runtime data that is kept while the code is being + /// assembled from the model. + /// + public sealed class BuildContext + { + private readonly IList errors = new List(); + private readonly List> sums = new List>(); + private readonly IList tokens = new ArrayList(); + private readonly ICollection vars = new OrderedSet(); + private readonly IList possibleDivisionsByZero = new List(); + internal IList PossibleDivisionsByZero + { + get + { + return possibleDivisionsByZero; + } + } + + /// + /// Initializes a new instance of the class. + /// + public BuildContext() : this(new BuildContextOptions()) { } + + + /// + /// Initializes a new instance of the class. + /// + /// Build options. + public BuildContext(BuildContextOptions options) + { + Options = options; + } + + /// + /// Options used for building the code. + /// + internal BuildContextOptions Options { get; private set; } + + /// + /// Errors encountered during build. + /// + public IList Errors + { + get { return errors; } + } + + /// + /// Variables that have been defined during build. + /// + public ICollection Vars + { + get { return vars; } + } + + /// + /// Tokens that have been met during build. + /// + public IList Tokens + { + get { return tokens; } + } + + /// + /// Returns the last token encountered, or null if there are none. + /// + public Object LastToken + { + get + { + return tokens.Count > 0 ? tokens[tokens.Count - 1] : null; + } + } + + /// + /// Returns true if last token suggests the use of times before identifier. + /// + public bool LastTokenRequiresTimes + { + get + { + if (LastToken == null) return false; + + // times is required in all cases, except when + // - last token was an operator and not a closing brace + object t = LastToken; + bool isMo = t is Mo; + bool isClosing = false; + if (isMo) + isClosing = ((Mo)t).IsClosingBrace; + + if (isClosing) + { + Trace.WriteLine("No * due to closing brace."); + return true; + } + if (isMo) + { + Trace.WriteLine("No * as last token is Mo."); + return false; + } + + if (t is Msup | t is Mrow) return false; + + Trace.WriteLine("Need *. Last token is " + t.GetType().Name); + return true; + } + } + + /// + /// Indicates whether we are on the right-hand side (after =) of the equation. + /// + public bool OnRhs + { + get + { + // note: completely wrong. = can appear in, e.g., sum subscripts + foreach (var v in tokens) + if (v is Mo && (v as Mo).IsEquals) + return true; + return false; + } + } + + + public IList> Sums + { + get + { + return sums; + } + } + public bool InMatrixDeterminate { get; set; } + + //string: function name, bool: go through bracket + public Stack> BuiltinFuncPair = new Stack>(); + + /// + /// Adds a sum. + /// + /// The sum. + public void AddSum(ISum sum) + { + for (char c = 'a'; c < 'z'; ++c) + { + char c1 = c; + if (!vars.Contains(c1.ToString()) && + sums.FindIndex(i => i.Second == c1) == -1) + { + sums.Add(new Pair(sum, c)); + vars.Add(c.ToString()); + return; + } + } + // todo: make this more civil later on + throw new Exception("Out of variables!"); + } + + public char GetSumIdentifier(ISum sum) + { + int idx = sums.FindIndex(i => i.First == sum); + if (idx != -1) + return sums[idx].Second; + else return '?'; + } + } + + internal class BuildContextSum + { + private IBuildable[] initTokens; + private IBuildable[] limitTokens; + private IBuildable[] statement; + } + + public enum EquationDataType + { + Float, + Double, + Decimal + } +} diff --git a/MathMLToCSharpLib.o/BuildContextOptions.cs b/MathMLToCSharpLib.o/BuildContextOptions.cs new file mode 100644 index 0000000..53a3e97 --- /dev/null +++ b/MathMLToCSharpLib.o/BuildContextOptions.cs @@ -0,0 +1,210 @@ +using System; +using System.ComponentModel; + +namespace MathMLToCSharpLib +{ + /// + /// Options used for building the code. + /// + [Serializable] + public sealed class BuildContextOptions : INotifyPropertyChanged + { + private bool deltaPartOfIdent = true; + private EquationDataType eqnDataType = EquationDataType.Double; + private bool greekToRoman; + private int maxInlinePower = 2; + private bool numberPostfix; + private bool parallelize; + private bool reduceFractions = true; + private bool replaceEWithMathE; + private bool replaceExpWithMathExp = true; + private bool replacePiWithMathPI; + private bool singleLetterVars = true; + private bool treatSigmaAsSum = true; + + public BuildContextOptions() + { + ReplacePiWithMathPI = true; + } + + /// + /// When set, prevents the builder from adding variables or appending * in front of them. + /// Also, superscripts are treated as ordinary Mn elements when this is set. + /// + public bool SubscriptMode { get; set; } + + [Category("Substitution")] + [DisplayName("Replace exp with Math.Exp")] + [Description("Instances of exp(x) are replaced with Math.Exp(x).")] + public bool ReplaceExpWithMathExp + { + get + { + return replaceExpWithMathExp; + } + set + { + replaceExpWithMathExp = value; + NotifyPropertyChanged("ReplaceExpWithMathExp"); + } + } + + [Category("Substitution")] + [DisplayName("Max Inline Power")] + public int MaxInlinePower + { + get + { + return maxInlinePower; + } + set + { + maxInlinePower = value; + NotifyPropertyChanged("MaxInlinePower"); + } + } + + [Category("Substitution")] + [DisplayName("Replace π with Math.PI")] + [Description("Instances of Math.Pow(n, 2) are replaced with n*n.")] + public bool ReplacePiWithMathPI + { + get { return replacePiWithMathPI; } + set + { + replacePiWithMathPI = value; + NotifyPropertyChanged("ReplacePiWithMathPI"); + } + } + + [Category("Substitution")] + [DisplayName("Replace e with Math.E")] + public bool ReplaceEWithMathE + { + get { return replaceEWithMathE; } + set + { + replaceEWithMathE = value; + NotifyPropertyChanged("ReplaceeWithMathE"); + } + } + + [DisplayName("Single-letter variables")] + [Category("Keep Options")] + [Description("When set, multi-letter variables (e.g., 'abc') will be split into single-letter ones (e.g., 'a', 'b', 'c').")] + public bool SingleLetterVars + { + get { return singleLetterVars; } + set + { + singleLetterVars = value; + NotifyPropertyChanged("SingleLetterVars"); + } + } + + [Category("Substitution")] + [DisplayName("Greek to Roman")] + [Description("When set, Greek identifiers will be replaced with their romanized names.")] + public bool GreekToRoman + { + get { return greekToRoman; } + set + { + greekToRoman = value; + NotifyPropertyChanged("GreekToRoman"); + } + } + + [DisplayName("Data type")] + [Description("The default data type used by the code generator")] + public EquationDataType EqnDataType + { + get { return eqnDataType; } + set + { + eqnDataType = value; + NotifyPropertyChanged("EqnDataType"); + } + } + + [DisplayName("Number postfix")] + [Description("When set, all numbers will have a postfix corresponding to their data type.")] + public bool NumberPostfix + { + get { return numberPostfix; } + set + { + numberPostfix = value; + NotifyPropertyChanged("NumberPostfix"); + } + } + + [DisplayName("Keep Δ attached")] + [Category("Keep Options")] + [Description("When set, the letter Δ will be stuck to the letter that follows it (if any).")] + public bool DeltaPartOfIdent + { + get { return deltaPartOfIdent; } + set + { + deltaPartOfIdent = value; + NotifyPropertyChanged("DeltaPartOfIdent"); + } + } + + [Category("Substitution")] + [DisplayName("Treat Σ as sum")] + [Description("When set, the letter Σ will be treated as a summation operator, and appropriate code will be emitted.")] + public bool TreatSigmaAsSum + { + get { return treatSigmaAsSum; } + set + { + treatSigmaAsSum = value; + NotifyPropertyChanged("TreatSigmaAsSum"); + } + } + + [Description("When set, causes all loops to be defined using Parallel Extensions.")] + public bool Parallelize + { + get + { + return parallelize; + } + set + { + parallelize = value; + NotifyPropertyChanged("Parallelize"); + } + } + + [Category("Substitution")] + [DisplayName("Reduce Fractions")] + [Description("When set, simple fractions (e.g., 1/2) are substituted by their result (e.g., 0.5).")] + public bool ReduceFractions + { + get { return reduceFractions; } + set + { + reduceFractions = value; + NotifyPropertyChanged("ReduceFractions"); + } + } + + #region INotifyPropertyChanged Members + + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + private void NotifyPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + { + PropertyChanged(this, + new PropertyChangedEventArgs(propertyName)); + } + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Conversion.cs b/MathMLToCSharpLib.o/Conversion.cs new file mode 100644 index 0000000..91502b0 --- /dev/null +++ b/MathMLToCSharpLib.o/Conversion.cs @@ -0,0 +1,33 @@ +using MathMLToCSharpLib.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace MathMLToCSharpLib +{ + public class Conversion + { + public Conversion() : this(new BuildContextOptions()) { } + public Conversion(BuildContextOptions options) + { + Singleton.Instance.Options = options; + } + + public string ParseAndOutput(string mathML) + { + IBuildable b = Parser.Parse(XElement.Parse(mathML)); + StringBuilder sb = new StringBuilder(); + b.Visit(sb, new BuildContext(Singleton.Instance.Options)); + return sb.ToString(); + } + + public BuildContextOptions Options + { + get { return Singleton.Instance.Options; } + set { Singleton.Instance.Options = value; } + } + + } +} diff --git a/MathMLToCSharpLib.o/Entities/Annotation.cs b/MathMLToCSharpLib.o/Entities/Annotation.cs new file mode 100644 index 0000000..63329d2 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Annotation.cs @@ -0,0 +1,16 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + public class Annotation : WithTextContent + { + public Annotation(string content):base(content) {} + public override void Visit(StringBuilder sb, BuildContext bc) + { + if (!string.IsNullOrEmpty(content) && content.Length > 0) + { + // no way - MathType annotations are just junk + } + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/BuildablePlainSum.cs b/MathMLToCSharpLib.o/Entities/BuildablePlainSum.cs new file mode 100644 index 0000000..31db1c9 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/BuildablePlainSum.cs @@ -0,0 +1,64 @@ +using System.Text; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Schema; + +namespace MathMLToCSharpLib.Entities +{ + public class BuildablePlainSum : ISum + { + private readonly IBuildable expression; + public BuildablePlainSum(IBuildable expression) + { + this.expression = expression; + } + + #region ISum Members + + public void Visit(StringBuilder sb, BuildContext bc) + { + sb.Append(bc.GetSumIdentifier(this)); + } + + public string Expression(BuildContext context) + { + // get the target + StringBuilder target = new StringBuilder(); + expression.Visit(target, context); + + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("for (int i{0} = 0; i{0} < {1}.Length; ++i{0})", + context.GetSumIdentifier(this), target); + sb.AppendLine(); + sb.Append(" "); + sb.AppendFormat("{0} += {1}[i{0}];", context.GetSumIdentifier(this), target); + return sb.ToString(); + } + + /*public XElement ToXElement() + { + return new XElement(this.GetType().Name, expression.ToXElement()); + }*/ + + #region IXmlSerializable Members + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + expression.ReadXml(reader); + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteStartElement(this.GetType().Name); + expression.WriteXml(writer); + writer.WriteEndElement(); + } + #endregion + + #endregion + } +} diff --git a/MathMLToCSharpLib.o/Entities/IBuildable.cs b/MathMLToCSharpLib.o/Entities/IBuildable.cs new file mode 100644 index 0000000..7496409 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/IBuildable.cs @@ -0,0 +1,20 @@ +using System.Text; +using System.Xml.Linq; +using System.Xml.Serialization; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// This interface must be implemented by all types of expression + /// objects in the tree. + /// + public interface IBuildable : IXmlSerializable + { + /// + /// Builds a textual (C#) representation of the model existing at the root of this node. + /// + /// The builder that aggregates the text. + /// Run-time build information. + void Visit(StringBuilder sb, BuildContext bc); + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/ISum.cs b/MathMLToCSharpLib.o/Entities/ISum.cs new file mode 100644 index 0000000..690e640 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/ISum.cs @@ -0,0 +1,7 @@ +namespace MathMLToCSharpLib.Entities +{ + public interface ISum : IBuildable + { + string Expression(BuildContext context); + } +} diff --git a/MathMLToCSharpLib.o/Entities/Math.cs b/MathMLToCSharpLib.o/Entities/Math.cs new file mode 100644 index 0000000..92fa197 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Math.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Root element in MathML. Not to be confused with System.Math. + /// + public class Math : WithBuildableContents, IXmlSerializable + { + public Math() { } + public Math(string content) : base(new IBuildable[] { }) { /* just in case */ } + public Math(IBuildable content) : base(new[] { content }) { } + public Math(IBuildable[] contents) : base(contents) { } + + #region IXmlSerializable Methods + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + List tempContent = new List(); + while(reader.Read()) + { + IBuildable element = null; + if (reader.IsStartElement() & reader.Name != "Math") + { + Type type = Type.GetType(this.GetType().Namespace + "." + reader.Name); + element = (IBuildable)Activator.CreateInstance(type); + element.ReadXml(reader); + tempContent.Add(element); + } + else if (reader.NodeType == XmlNodeType.EndElement & reader.Name == this.GetType().Name) + { + contents = tempContent.ToArray(); + } + } + + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteStartDocument(); + writer.WriteStartElement(this.GetType().Name); + foreach (IBuildable item in contents) + item.WriteXml(writer); + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + #endregion + + public override void Visit(StringBuilder sb, BuildContext context) + { + base.Visit(sb, context); + + // todo: this assumes that there is only one statement, which may not be the case + if (sb.ToString().Length > 0) + sb.Append(";"); + + // sums + int j = context.Sums.Count; + if (j > 0) + { + var builder = new StringBuilder(); + foreach (var v in context.Sums) + { + builder.AppendLine(v.First.Expression(context)); + } + sb.Insert(0, builder.ToString()); + } + + // variables + int i = context.Vars.Count; + if (i > 0) + { + var builder = new StringBuilder(); + foreach (var v in context.Vars) + { + builder.Append(Enum.GetName(typeof(EquationDataType), context.Options.EqnDataType).ToLower()); + builder.Append(" "); + builder.Append(v); + builder.Append(" = 0.0"); // this is a *must* + if (context.Options.NumberPostfix) + builder.Append(Semantics.postfixForDataType(context.Options.EqnDataType)); + builder.AppendLine(";"); + } + foreach (IBuildable ib in context.PossibleDivisionsByZero) + { + var b = new StringBuilder(); + ib.Visit(b, new BuildContext(Singleton.Instance.Options)); + builder.AppendFormat("Debug.Assert({0} != 0, \"Expression {0} is about to cause division by zero.\");", + b); + builder.AppendLine(); + } + sb.Insert(0, builder.ToString()); + } + } + + + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mfenced.cs b/MathMLToCSharpLib.o/Entities/Mfenced.cs new file mode 100644 index 0000000..c94b73d --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mfenced.cs @@ -0,0 +1,25 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Code in brackets. + /// + public class Mfenced : WithBuildableContent + { + public Mfenced() { } + public Mfenced(IBuildable content) : base(content) { } + public override void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + + if (bc.LastTokenRequiresTimes) + sb.Append("*"); + + // todo: brackets do not need to be added if there is only one element (e.g., braces around a matrix) + sb.Append("("); + base.Visit(sb, bc); + sb.Append(")"); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mfrac.cs b/MathMLToCSharpLib.o/Entities/Mfrac.cs new file mode 100644 index 0000000..e73bb91 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mfrac.cs @@ -0,0 +1,67 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Fraction. + /// + // note: it might be worthwhile somehow differentiating between fraction and + // ordinary division, e.g., having fraction parts evaluate as temporary variables. + public class Mfrac : WithBinaryContent + { + public Mfrac() { } + public Mfrac(IBuildable first, IBuildable second) : base(first, second) { } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + + bool needReduce = false; + // note: the use of double is a judgement call here + double result = 0.0; + if (bc.Options.ReduceFractions) + { + Mrow row1 = first as Mrow; + Mrow row2 = second as Mrow; + if (row1 != null && row2 != null && row1.Contents.Length > 0 && row2.Contents.Length > 0) + { + Mn mn1 = row1.Contents[0] as Mn; + Mn mn2 = row2.Contents[0] as Mn; + if (mn1 != null && mn2 != null) + { + try + { + double _1, _2; + if (double.TryParse(mn1.Content, out _1) && + double.TryParse(mn2.Content, out _2) && + _2 != 0.0) + { + result = _1 / _2; + needReduce = true; + } + } + catch + { + } + } + } + } + + if (needReduce) + { + sb.Append(result); + } + else + { + sb.Append("(("); + first.Visit(sb, bc); + sb.Append(") / ("); + second.Visit(sb, bc); + sb.Append("))"); + + // add to checks + bc.PossibleDivisionsByZero.Add(second); + } + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mi.cs b/MathMLToCSharpLib.o/Entities/Mi.cs new file mode 100644 index 0000000..c2473d0 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mi.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; +using Wintellect.PowerCollections; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Identifier (a.k.a. variable). + /// + public class Mi : WithTextContent + { + public Mi() { } + public Mi(string content) : base(content) { } + + /// + /// Returns true if this is a function not supported by System.Math. + /// + public bool IsUnsupportedFunction + { + get + { + return content == "sec" || content == "sech" || + content == "csc" || content == "csch" || + content == "cot" || content == "coth"; + } + } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + if (string.IsNullOrEmpty(content)) + return; + + //postfix Built-in function detection + if (content == "Eigenvectors" || content == "Eigenvalues") + { + bc.BuiltinFuncPair.Push(new Pair(content, false)); + bc.Tokens.Add(this); + return; + } + + // get every single variable, unless this is a function name rather than a variable name + // this assumes that nobody intends to call a variable, e.g., 'sin' + // also, the variable is not converted if it starts with a delta and we've chosen to + // keep delta as part of identifier + List vars = (bc.Options.SingleLetterVars && !Semantics.rep.ContainsKey(content) && + !(bc.Options.DeltaPartOfIdent && content[0] == '∆')) + ? + new List(Array.ConvertAll(content.ToCharArray(), c => c.ToString())) + : + new List { content }; + + for (int i = 0; i < vars.Count; i++) + { + string varName = vars[i]; + + bool needReplace = false; + string replaceTerm = string.Empty; + if (Semantics.rep.ContainsKey(varName)) + { + Pair p = Semantics.rep[varName]; + replaceTerm = p.First; + if (string.IsNullOrEmpty(p.Second)) + needReplace = true; + else + { + Type bct = typeof(BuildContextOptions); + PropertyInfo pi = bct.GetProperty(p.Second); + Debug.Assert(pi != null); + needReplace = (bool)pi.GetValue(bc.Options, null); + } + } + + // if variable not subject to replacement, check to see if it requires conversion from greek + if (!needReplace && varName.Length == 1 && bc.Options.GreekToRoman) + { + varName = Util.GreekLetterName(varName[0]); + } + + // can only declare variable if + // 1) if it hasn't already been declared + // 2) if there needn't be a replacement, e.g., of e with Math.E + // 3) it hasn't been explicitly blocked + if (!bc.Vars.Contains(varName) && !needReplace && !bc.Options.SubscriptMode) + bc.Vars.Add(varName); + + // use implicit multiplication if necessary + if (bc.LastTokenRequiresTimes && !bc.Options.SubscriptMode) + sb.Append("*"); + // add type to context + if (needReplace) + { + sb.Append(replaceTerm); + } + else + { + sb.Append(varName); + } + // unless this is the last variable, add multiplication sign after it + if (i + 1 != vars.Count) + sb.Append("*"); + } + + bc.Tokens.Add(this); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mn.cs b/MathMLToCSharpLib.o/Entities/Mn.cs new file mode 100644 index 0000000..e0b95b7 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mn.cs @@ -0,0 +1,50 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Number. + /// + public class Mn : WithTextContent + { + public Mn() { } + public Mn(string content) : base(content) { } + + /// + /// Returns true if the content is an integer greater than 1 (i.e., 2, 3, etc.) + /// + public bool IsIntegerGreaterThan1 + { + get + { + try + { + double d = double.Parse(content); + if (System.Math.Floor(d) == d) + if (d > 1.0) + return true; + return false; + } + catch + { + return false; + } + } + } + + public int IntegerValue + { + get + { + return int.Parse(content); + } + } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + base.Visit(sb, bc); + if (bc.Options.NumberPostfix && !bc.Options.SubscriptMode) + sb.Append(Semantics.postfixForDataType(bc.Options.EqnDataType)); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mo.cs b/MathMLToCSharpLib.o/Entities/Mo.cs new file mode 100644 index 0000000..c6fe6b3 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mo.cs @@ -0,0 +1,181 @@ +using System.Collections.Specialized; +using System.Diagnostics; +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Operator. + /// + public class Mo : WithTextContent + { + private static readonly StringDictionary rep; + static Mo() + { + // replacements in lieu of the actual operator + rep = new StringDictionary + { + {"!", ".Factorial()"}, + {"-", "-"}, // this one is *really* nasty :) + {"−", "-"}, // this one is also annoying (thanks MathType) + {"÷", "/"}, + {"×", "*"}, + {"∗", "*"}, // << unpleasant too! + {"[", "("}, + {"]", ")"}, + {"{", "("}, + {"}", ")"} + }; + } + + public Mo() { } + public Mo(string content) : base(content) { } + + /// + /// Returns true if this is an equals operator. + /// + /// + /// Useful for determining whether we are on the RHS or LHS of an assignment. + /// + public bool IsEquals + { + get + { + return content == "="; + } + } + + /// + /// Returns true if this is a closing brace. + /// + public bool IsClosingBrace + { + get + { + return content == ")"; + } + } + + /// + /// Returns true if this is an opening brace. + /// + public bool IsOpeningBrace + { + get + { + return content == "("; + } + } + + /// + /// Returns true if this is a printable operator. + /// + public bool IsPrintableOperator + { + get + { + return (content != "\u2061" && content != "\u2062" && content != "\u2063"); + } + } + + /// + /// Gets a value indicating whether the content is uppercase sigma. + /// + /// true if the content is sigma; otherwise, false. + public bool IsSigma + { + get + { + return content == "∑"; + } + } + + /// + /// Gets a value indicating whether the content is uppercase delta. + /// + /// true if the content is delta; otherwise, false. + public bool IsDelta + { + get + { + return content == "∆"; + } + } + + /// + /// Gets a value indicating whether this instance is times or star. + /// + /// + /// true if this instance is times or star; otherwise, false. + /// + public bool IsTimesOrStar + { + get + { + return "*∗".Contains(content); + } + } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + //| matrix | detrminate + if (content == "|") + { + if (bc.InMatrixDeterminate) + { + sb.Append(".Determinant()"); + bc.InMatrixDeterminate = false; + } + else + bc.InMatrixDeterminate = true; + return; + } + + //Built in function + if (content == "(" && bc.BuiltinFuncPair.Count != 0 && bc.BuiltinFuncPair.Peek().Second == false) + { + var pr = bc.BuiltinFuncPair.Pop(); + pr.Second = true; + bc.BuiltinFuncPair.Push(pr); + return; + } + else if (content == ")" && bc.BuiltinFuncPair.Count != 0 && bc.BuiltinFuncPair.Peek().Second == true) + { + switch (bc.BuiltinFuncPair.Peek().First) + { + default: + sb.Append(")"); + break; + case "Eigenvalues": + sb.Append(".Evd().EigenValues"); + break; + case "Eigenvectors": + sb.Append(".Evd().EigenVectors"); + break; + } + bc.BuiltinFuncPair.Pop(); + return; + } + + // if we are in subscript mode, adding an operator is not necessary, but neither can we ignore it + if (bc.Options.SubscriptMode) + { + sb.Append("_"); + } + else if (rep.ContainsKey(content)) + sb.Append(rep[content]); + else + { + Trace.Assert(content.Length == 1, "We only support 1-char operators (for now)."); + if (IsPrintableOperator) + { + if (IsOpeningBrace && bc.LastTokenRequiresTimes) + sb.Append("*"); + + sb.Append(content); + } + } + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mroot.cs b/MathMLToCSharpLib.o/Entities/Mroot.cs new file mode 100644 index 0000000..e8d1304 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mroot.cs @@ -0,0 +1,22 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Radical. These do not exist in C#, so cubic root of N is encoded as Math.Pow(N, 1/3). + /// + public class Mroot : WithBinaryContent + { + public Mroot() { } + public Mroot(IBuildable first, IBuildable second) : base(first, second) { } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + sb.Append("Math.Pow("); + first.Visit(sb, bc); + sb.Append(", 1 / "); + second.Visit(sb, bc); + sb.Append(")"); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mrow.cs b/MathMLToCSharpLib.o/Entities/Mrow.cs new file mode 100644 index 0000000..f3fa278 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mrow.cs @@ -0,0 +1,67 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// A fairly generic element that does not mean anything. + /// + public class Mrow : WithBuildableContents + { + public Mrow() { } + public Mrow(IBuildable[] content) : base(content) + { + + } + + public Mrow(IBuildable first, IBuildable second) : this(new[] { first, second }) + { + } + + public Mrow(IBuildable content) : this(new[] { content }) + { + } + + public IBuildable[] Contents + { + get { return contents; } + } + + /// + /// Returns true if this contains a single . + /// + public bool ContainsSingleMi + { + get + { + return contents.Length == 1 && contents[0] is Mi; + } + } + + /// + /// Gets a value indicating whether this Mrow contains a single Mn. + /// + /// true if this row contains a single Mn; otherwise, false. + public bool ContainsSingleMn + { + get + { + return contents.Length == 1 && contents[0] is Mn; + } + } + + /// + /// Returns true if this contains a single . + /// + public bool ContainsSingleMtable + { + get + { + return contents.Length == 1 && contents[0] is Mtable; + } + } + public override void Visit(StringBuilder sb, BuildContext context) + { + base.Visit(sb, context); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Msqrt.cs b/MathMLToCSharpLib.o/Entities/Msqrt.cs new file mode 100644 index 0000000..1faecec --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Msqrt.cs @@ -0,0 +1,22 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Square root. + /// + public class Msqrt : WithBuildableContents + { + public Msqrt() { } + public Msqrt(IBuildable content) : base(new[] { content }) { } + public Msqrt(IBuildable[] contents) : base(contents) { } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + sb.Append("Math.Sqrt("); + base.Visit(sb, bc); + sb.Append(")"); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mstyle.cs b/MathMLToCSharpLib.o/Entities/Mstyle.cs new file mode 100644 index 0000000..a6d6d0c --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mstyle.cs @@ -0,0 +1,9 @@ +namespace MathMLToCSharpLib.Entities +{ + class Mstyle : WithBuildableContents + { + public Mstyle() { } + public Mstyle(IBuildable content) : base(new[] { content }) { } + public Mstyle(IBuildable[] contents) : base(contents) { } + } +} diff --git a/MathMLToCSharpLib.o/Entities/Msub.cs b/MathMLToCSharpLib.o/Entities/Msub.cs new file mode 100644 index 0000000..37ead4e --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Msub.cs @@ -0,0 +1,36 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Subscript. + /// + public class Msub : WithBinaryContent + { + public Msub() { } + public Msub(IBuildable first, IBuildable second) : base(first, second) { } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + + bool last = bc.Options.SubscriptMode; + bc.Options.SubscriptMode = true; + + StringBuilder b = new StringBuilder(); + + first.Visit(b, bc); + b.Append("_"); + second.Visit(b, bc); + + string varName = b.ToString(); + if (!bc.Vars.Contains(varName)) + if (!last) // unless we are already in a subscript-entering mode... + bc.Vars.Add(varName); + + sb.Append(varName); + + bc.Options.SubscriptMode = last; + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Msubsup.cs b/MathMLToCSharpLib.o/Entities/Msubsup.cs new file mode 100644 index 0000000..9b1a51a --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Msubsup.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Subscript and superscript one one variable. + /// + public class Msubsup : WithBuildableContents + { + public Msubsup() { } + public Msubsup(IBuildable[] contents) + { // handle star superscript + List localCopy = new List(contents); + if (localCopy.Count == 3) + { + Mo mo = localCopy[2] as Mo; + if (mo != null && mo.IsTimesOrStar) + { + Mi subscript = localCopy[1] as Mi; + if (subscript != null) + { + subscript.Content += Semantics.starPrefix; + localCopy.RemoveAt(2); + } + else + { + // maybe the subscript is an mrow + Mrow row = localCopy[1] as Mrow; + if (row != null && row.LastElement != null && row.LastElement is WithTextContent) + { + WithTextContent lastElem = (WithTextContent)row.LastElement; + lastElem.Content += Semantics.starPrefix; + localCopy.RemoveAt(2); + } + } + } + } + base.contents = localCopy.ToArray(); + } + public override void Visit(StringBuilder sb, BuildContext context) + { + Debug.Assert(contents.Length == 2 || contents.Length == 3); + Msub sub = new Msub(contents[0], contents[1]); + switch (contents.Length) + { + case 2: + sub.Visit(sb, context); + break; + case 3: + Msup sup = new Msup(sub, contents[2]); + sup.Visit(sb, context); + break; + default: + throw new ApplicationException("Incorrect number of arguments in Msubsup"); + } + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Msup.cs b/MathMLToCSharpLib.o/Entities/Msup.cs new file mode 100644 index 0000000..51969d9 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Msup.cs @@ -0,0 +1,99 @@ +using System.Text; +using Wintellect.PowerCollections; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Superscript. Taken to mean 'power of' unless the superscript parses into a *. + /// + public class Msup : WithBinaryContent + { + public Msup() { } + public Msup(IBuildable first, IBuildable second) : base(first, second) { } + + public Pair Values + { + get + { + return new Pair(first, second); + } + } + + public override void Visit(StringBuilder sb, BuildContext bc) + { + if (bc.Options.SubscriptMode) + { + // separate by double underscore to differentiate from Msub and prevent clashes + first.Visit(sb, bc); + sb.Append("__"); + second.Visit(sb, bc); + } + else if (second is Mo && (second as Mo).IsTimesOrStar && first is Mi) + { + // this isn't really a superscript - it's part of the variable + Mi mi = (Mi)first; + Mi newMi = new Mi(mi.Content + Semantics.starPrefix); + newMi.Visit(sb, bc); + } + else + { + if ((first is Mrow) && ((first as Mrow).ContainsSingleMtable)) + { + if ((second is Mrow) && ((second as Mrow).ContainsSingleMi) && ((second as Mrow).LastElement as Mi).Content == "T") + { + first.Visit(sb, bc); + sb.Append(".Transpose()"); + } + else if ((second is Mrow) && ((second as Mrow).ContainsSingleMn) && ((second as Mrow).LastElement as Mn).IsIntegerGreaterThan1) + { + first.Visit(sb, bc); + sb.Append(".Power("); + second.Visit(sb, bc); + sb.Append(")"); + } + else if ((second is Mrow) && ((second as Mrow).ContainsSingleMn) && ((second as Mrow).LastElement as Mn).Content == "-1") + { + first.Visit(sb, bc); + sb.Append(".Inverse()"); + } + bc.Tokens.Add(this); + return; + } + + if (bc.LastTokenRequiresTimes) + sb.Append("*"); + + // determine whether power must be inlined + bool firstIsMrowMi = (first is Mrow) && ((first as Mrow).ContainsSingleMi); + bool secondIsIntegralPower = (second is Mrow) && ((second as Mrow).ContainsSingleMn) && + ((second as Mrow).LastElement as Mn).IsIntegerGreaterThan1; + bool mustInlinePower = false; + int power = 0; + if (secondIsIntegralPower) + { + power = ((second as Mrow).LastElement as Mn).IntegerValue; + mustInlinePower = power <= bc.Options.MaxInlinePower; + } + if (mustInlinePower) + { + for (int i = 0; i < power; ++i) + { + if (i != 0 && (first is Mrow) && ((first as Mrow).ContainsSingleMn)) + sb.Append("*"); //for the case mn^2 not appended * automatically + first.Visit(sb, bc); // * sign appended automatically + } + } + else + { + sb.Append("Math.Pow("); + first.Visit(sb, bc); + sb.Append(", "); + second.Visit(sb, bc); + sb.Append(")"); + } + } + + bc.Tokens.Add(this); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mtable.cs b/MathMLToCSharpLib.o/Entities/Mtable.cs new file mode 100644 index 0000000..2ea1424 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mtable.cs @@ -0,0 +1,33 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// A table. Typically used for matrices. + /// + public class Mtable : WithBuildableContents + { + static int level = 0; + public Mtable() { } + public Mtable(IBuildable[] contents) : base(contents) { } + public override void Visit(StringBuilder sb, BuildContext context) + { + level++; + if (level == 1) + sb.Append("DenseMatrix.OfArray(new double[,] {");//matrix + else + sb.Append("{");//matrix + for (int i = 0; i < contents.Length; ++i) + { + contents[i].Visit(sb, context); + if (i + 1 != contents.Length) + sb.Append(", "); + } + if (level == 1) + sb.Append("})"); + else + sb.Append("}"); + level--; + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mtd.cs b/MathMLToCSharpLib.o/Entities/Mtd.cs new file mode 100644 index 0000000..338ca3e --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mtd.cs @@ -0,0 +1,18 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Table cell. + /// + public class Mtd : WithBuildableContent + { + public Mtd() { } + public Mtd(IBuildable content) : base(content) { } + public override void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + content.Visit(sb, bc); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Mtr.cs b/MathMLToCSharpLib.o/Entities/Mtr.cs new file mode 100644 index 0000000..68552c2 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Mtr.cs @@ -0,0 +1,25 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Table row. + /// + public class Mtr : WithBuildableContents + { + public Mtr() { } + public Mtr(IBuildable[] contents) : base(contents) { } + public Mtr(IBuildable first, IBuildable second) : base(new[] { first, second }) { } + public override void Visit(StringBuilder sb, BuildContext context) + { + sb.Append("{"); + for (int i = 0; i < contents.Length; ++i) + { + contents[i].Visit(sb, context); + if (i + 1 != contents.Length) + sb.Append(", "); + } + sb.Append("}"); + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/Munderover.cs b/MathMLToCSharpLib.o/Entities/Munderover.cs new file mode 100644 index 0000000..da15f0a --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Munderover.cs @@ -0,0 +1,30 @@ +using System.Text; + +namespace MathMLToCSharpLib.Entities +{ + class Munderover : WithBuildableContents + { + public Munderover() { } + public Munderover(IBuildable[] contents) : base(contents) { } + public override void Visit(StringBuilder sb, BuildContext context) + { + if (contents.Length == 3) + { + // is the first one an operator? + Mo mo = contents[0] as Mo; + if (mo != null) + { + if (mo.IsSigma) + { + //todo: summation! + + } + } + } + else + { + sb.Append("Munderover must have 3 items"); + } + } + } +} diff --git a/MathMLToCSharpLib.o/Entities/Null.cs b/MathMLToCSharpLib.o/Entities/Null.cs new file mode 100644 index 0000000..5147297 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Null.cs @@ -0,0 +1,9 @@ +using System; + +namespace MathMLToCSharpLib.Entities +{ + [Obsolete("Do not use", true)] + class Null + { + } +} diff --git a/MathMLToCSharpLib.o/Entities/Semantics.cs b/MathMLToCSharpLib.o/Entities/Semantics.cs new file mode 100644 index 0000000..2bd6f40 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/Semantics.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.Collections.Specialized; +using Wintellect.PowerCollections; + +namespace MathMLToCSharpLib.Entities +{ + /// + /// Completely spurious element that MathType 6 insists on generating. + /// + public class Semantics : WithBuildableContents + { + internal const string starPrefix = "_star"; + + /// + /// Functions which need replacement. + /// + internal static readonly List knownFuncts; + + /// + /// Symbols and function names that can be replaced. + /// + internal static readonly Dictionary> rep; + /// + /// Trig functions which have an inverse, and their inverse values. + /// + internal static StringDictionary inverseTrigs; + + + static Semantics() + { + rep = new Dictionary> + { + {"∞", new Pair("double.MaxValue", null)}, + {"π", new Pair("Math.PI", "ReplacePiWithMathPI")}, + {"e", new Pair("Math.E", "ReplaceEWithMathE")}, + {"exp", new Pair("Math.Exp", "ReplaceExpWithMathExp")}, + {"*", new Pair("star", null)}, + + // functions + {"cos", new Pair("Math.Cos(", null)}, + {"cosh", new Pair("Math.Cosh(", null)}, + {"sec", new Pair("1/Math.Cos(", null)}, + {"sech", new Pair("1/Math.Cosh(", null)}, + {"sin", new Pair("Math.Sin(", null)}, + {"sinh", new Pair("Math.Sinh(", null)}, + {"csc", new Pair("1/Math.Sin(", null)}, + {"csch", new Pair("1/Math.Sinh(", null)}, + {"tan", new Pair("Math.Tan(", null)}, + {"tanh", new Pair("Math.Tanh(", null)}, + {"cot", new Pair("1/Math.Tan(", null)}, + {"coth", new Pair("1/Math.Tanh(", null)}, + {"ln", new Pair("Math.Log(", null)}, + {"log", new Pair("Math.Log10(", null)}, + + // inverse trig functions + {"arcsin", new Pair("Math.Asin(", null)}, + {"arccos", new Pair("Math.Acos(", null)}, + {"arctan", new Pair("Math.Atan(", null)}, + {"arccsc", new Pair("Math.Asin(1/", null)}, + {"arcsec", new Pair("Math.Acos(1/", null)}, + {"arccot", new Pair("Math.Atan(1/", null)} + }; + + inverseTrigs = new StringDictionary + { + {"sin", "arcsin"}, + {"cos", "arccos"}, + {"tan", "arctan"}, + {"csc", "arccsc"}, + {"sec", "arcsec"}, + {"cot", "arccot"} + }; + + knownFuncts = new List + { + "cos", + "cosh", + "arcsin", + "sec", + "sech", + "arcsec", + "sin", + "sinh", + "arcsin", + "csc", + "csch", + "arccsc", + "tan", + "tanh", + "arctan", + "cot", + "coth", + "arccot", + "ln", + "log", + "exp" // exponent, should be user-selectable + }; + } + + public Semantics(IBuildable[] contents) : base(contents) {} + + internal static string postfixForDataType(EquationDataType type) + { + switch (type) + { + case EquationDataType.Decimal: + return "M"; + case EquationDataType.Float: + return "f"; + default: + return string.Empty; + } + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/WithBinaryContent.cs b/MathMLToCSharpLib.o/Entities/WithBinaryContent.cs new file mode 100644 index 0000000..d88f417 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/WithBinaryContent.cs @@ -0,0 +1,78 @@ +using System; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using Wintellect.PowerCollections; + +namespace MathMLToCSharpLib.Entities +{ + public abstract class WithBinaryContent : IBuildable + { + public IBuildable first, second; + + protected WithBinaryContent() { } + protected WithBinaryContent(IBuildable first, IBuildable second) + { + this.first = first; + this.second = second; + } + + public Pair Contents + { + get + { + return new Pair(first, second); + } + } + + #region IBuildable Members + + public abstract void Visit(StringBuilder sb, BuildContext bc); + + /*public XElement ToXElement() + { + return new XElement(this.GetType().Name, first.ToXElement(), second.ToXElement()); + }*/ + + #region IXmlSerializable Members + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + int i = 0; + while (reader.Read()) + { + if (reader.IsStartElement()) + { + Type type = Type.GetType(this.GetType().Namespace + "." + reader.Name); + IBuildable element = (IBuildable)Activator.CreateInstance(type); + element.ReadXml(reader); + + if (i == 0) + first = element; + else + second = element; + i++; + } + else if (reader.NodeType == XmlNodeType.EndElement & reader.Name == this.GetType().Name) + { + return; + } + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteStartElement(this.GetType().Name); + first.WriteXml(writer); + second.WriteXml(writer); + writer.WriteEndElement(); + } + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/WithBuildableContent.cs b/MathMLToCSharpLib.o/Entities/WithBuildableContent.cs new file mode 100644 index 0000000..748c6aa --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/WithBuildableContent.cs @@ -0,0 +1,65 @@ +using System; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Schema; + +namespace MathMLToCSharpLib.Entities +{ + public abstract class WithBuildableContent : IBuildable + { + protected IBuildable content; + + public WithBuildableContent() { } + protected WithBuildableContent(IBuildable content) + { + this.content = content; + } + + #region IBuildable Members + + public virtual void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + content.Visit(sb, bc); + } + + /*public XElement ToXElement() + { + return new XElement(this.GetType().Name, content.ToXElement()); + }*/ + + #region IXmlSerializable Members + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + while (reader.Read()) + { + if (reader.IsStartElement()) + { + Type type = Type.GetType(this.GetType().Namespace + "." + reader.Name); + content = (IBuildable)Activator.CreateInstance(type); + content.ReadXml(reader); + } + else if (reader.NodeType == XmlNodeType.EndElement & reader.Name == this.GetType().Name) + { + return; + } + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteStartElement(this.GetType().Name); + content.WriteXml(writer); + writer.WriteEndElement(); + } + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/WithBuildableContents.cs b/MathMLToCSharpLib.o/Entities/WithBuildableContents.cs new file mode 100644 index 0000000..adfc9fc --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/WithBuildableContents.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Schema; +using Wintellect.PowerCollections; + +namespace MathMLToCSharpLib.Entities +{ + public abstract class WithBuildableContents : IBuildable + { + public IBuildable[] contents; + protected WithBuildableContents() + { + } + + protected WithBuildableContents(IBuildable[] contents) + { + this.contents = contents; + + ReplaceInverseTrigFunctions(contents); + } + + public IBuildable LastElement + { + get + { + if (contents.Length > 0) + return contents[contents.Length - 1]; + return null; + } + } + + #region IBuildable Members + + public virtual void Visit(StringBuilder sb, BuildContext context) + { + bool containsFunct = false; + int numFuncts = 0; + + if (sb == null || context == null || contents == null || contents.Length == 0) + return; + + // copy contents into list + List ctxCopy = new List(contents); + + // check if the Sigma operator acts as a sum + if (context.Options.TreatSigmaAsSum) + { + // look for the plain sigma operator + int index; + while ((index = ctxCopy.FindIndex(a => (a is Mo && (a as Mo).IsSigma))) != -1) + { + // so long as it is not the last element + if (index != ctxCopy.Count - 1) + { + BuildablePlainSum bps = new BuildablePlainSum(ctxCopy[index + 1]); + ctxCopy.RemoveAt(index + 1); + ctxCopy[index] = bps; + + context.AddSum(bps); + } + } + } + + // check if delta appears before an + if (context.Options.DeltaPartOfIdent) + { + int index; + while ((index = ctxCopy.FindIndex(a => (a is Mo && (a as Mo).IsDelta))) != -1) + { + // check that it's not the last element + if (index != ctxCopy.Count - 1) + { + // check that the next element is + Mi mi = ctxCopy[index + 1] as Mi; + if (mi != null) + { + // change Mi's content to incorporate the delta + Mi newMi = new Mi("∆" + mi.Content); + ctxCopy[index + 1] = newMi; + // remove the delta + ctxCopy.RemoveAt(index); + Trace.WriteLine(newMi.Content); + } + } + } + } + else + { + // change delta from mo to mi + int index; + while ((index = ctxCopy.FindIndex(a => (a is Mo && (a as Mo).IsDelta))) != -1) + { + ctxCopy[index] = new Mi("∆"); + } + } + + // Scan for functions. + // If one is found, increment the number. Because nested + // functions which do not contain parentheses (e.g. sin sin x) + // are contained in the same row element (in non-Word generated markup), + // this allows us to determine how many close parens we need. + foreach (IBuildable v in ctxCopy) + { + if (v is Mi && Semantics.knownFuncts.Contains((v as Mi).Content)) + { + containsFunct = true; + numFuncts++; + } + } + + if (containsFunct) + { + // Only process if this is not the spurious function. + if (ctxCopy.Count != 1 || (!Semantics.knownFuncts.Contains((ctxCopy[0] as Mi).Content))) + { + foreach (IBuildable v in ctxCopy) + v.Visit(sb, context); + + for (; numFuncts > 0; numFuncts--) + sb.Append(")"); + } + } + // No function was found, so process in the normal fashion. + else + { + foreach (var v in ctxCopy) + v.Visit(sb, context); + } + } + + #region IXmlSerializable Members + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + List tempContent = new List(); + while(reader.Read()) + { + if (reader.IsStartElement()) + { + Type type = Type.GetType(this.GetType().Namespace + "." + reader.Name); + IBuildable element = (IBuildable)Activator.CreateInstance(type); + tempContent.Add(element); + element.ReadXml(reader); + } + else if (reader.NodeType == XmlNodeType.EndElement & reader.Name == this.GetType().Name) + { + contents = tempContent.ToArray(); + return; + } + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteStartElement(this.GetType().Name); + + foreach (IBuildable item in contents) + item.WriteXml(writer); + + writer.WriteEndElement(); + } + #endregion + #endregion + + /// + /// This function replaces all known inverse trig functions (in the msup blocks) + /// by inverse names, so that sin^-1 becomes arcsin. + /// + /// Buidlable contents + private static void ReplaceInverseTrigFunctions(IBuildable[] contents) + { + for (int i = 0; i < contents.Length; i++) + { + IBuildable c = contents[i]; + string trigFunction = string.Empty; + if (c is Msup) + { + bool funcIsTrig = false, radixIsNeg1 = false; + Pair terms = (c as Msup).Values; + if (terms.First is Mrow) + { + Mrow row1 = terms.First as Mrow; + if (row1.Contents.Length > 0 && row1.Contents[0] is Mi) + { + if (Semantics.inverseTrigs.ContainsKey((row1.Contents[0] as Mi).Content)) + { + trigFunction = (row1.Contents[0] as Mi).Content; + funcIsTrig = true; + } + } + } + if (terms.Second is Mrow) + { + Mrow row2 = terms.Second as Mrow; + StringBuilder sb = new StringBuilder(); + BuildContext bc = new BuildContext(); + row2.Visit(sb, bc); + if (sb.ToString() == "-1") + radixIsNeg1 = true; + } + // if this is an inverse function, replace an with an inverse + if (funcIsTrig && radixIsNeg1) + { + Mi mi = new Mi(Semantics.inverseTrigs[trigFunction]); + contents[i] = mi; + } + } + } + } + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Entities/WithTextContent.cs b/MathMLToCSharpLib.o/Entities/WithTextContent.cs new file mode 100644 index 0000000..4c0ba96 --- /dev/null +++ b/MathMLToCSharpLib.o/Entities/WithTextContent.cs @@ -0,0 +1,68 @@ +using System; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Schema; + +namespace MathMLToCSharpLib.Entities +{ + public abstract class WithTextContent : IBuildable + { + public string content; + protected WithTextContent() { } + protected WithTextContent(string content) + { + this.content = content; + } + + public string Content + { + get { return content; } + set { content = value; } + } + + + #region IBuildable Members + + public virtual void Visit(StringBuilder sb, BuildContext bc) + { + bc.Tokens.Add(this); + sb.Append(content); + } + + /*public XElement ToXElement() + { + return new XElement(this.GetType().Name, content); + }*/ + + #region IXmlSerializable Members + public XmlSchema GetSchema() + { + return null; + } + + public void ReadXml(XmlReader reader) + { + while (reader.Read()) + { + if (reader.HasValue) + { + content = reader.Value; + return; + } + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteStartElement(this.GetType().Name); + writer.WriteString(content); + writer.WriteEndElement(); + } + #endregion + + #endregion + + + } +} \ No newline at end of file diff --git a/MathMLToCSharpLib.o/MathMLToCSharpLib.csproj b/MathMLToCSharpLib.o/MathMLToCSharpLib.csproj new file mode 100644 index 0000000..0172090 --- /dev/null +++ b/MathMLToCSharpLib.o/MathMLToCSharpLib.csproj @@ -0,0 +1,81 @@ + + + + + Debug + AnyCPU + {3909E9A8-4E8A-4358-A8A9-3BA634090378} + Library + Properties + MathMLToCSharpLib + MathMLToCSharpLib + v3.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + .\PowerCollections.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MathMLToCSharpLib.o/MathMLToCSharpLib.nuspec b/MathMLToCSharpLib.o/MathMLToCSharpLib.nuspec new file mode 100644 index 0000000..05c7c72 --- /dev/null +++ b/MathMLToCSharpLib.o/MathMLToCSharpLib.nuspec @@ -0,0 +1,16 @@ + + + + MathMLToCSharp + 1.1.0 + PhilipXYC + PhilipXYC + https://github.com/philipxyc/MathMLToCSharp/blob/master/LICENSE + https://github.com/philipxyc/MathMLToCSharp + MathML to C# expression Converter + MathML to C# expression Converter + Added xml serialization support + Copyright 2018 + Math MathML Conversion Caculate + + \ No newline at end of file diff --git a/MathMLToCSharpLib.o/Parser.cs b/MathMLToCSharpLib.o/Parser.cs new file mode 100644 index 0000000..ae3993d --- /dev/null +++ b/MathMLToCSharpLib.o/Parser.cs @@ -0,0 +1,76 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; +using MathMLToCSharpLib.Entities; +using Math=MathMLToCSharpLib.Entities.Math; + +namespace MathMLToCSharpLib +{ + public static class Parser + { + /// + /// Capitalizes the initial letter of an identifier. + /// + /// The identifier. + /// The modified identifier. + internal static string camel(string id) + { + return "" + char.ToUpper(id[0], CultureInfo.InvariantCulture) + id.Substring(1); + } + + /// + /// Parses the XML tree and returns the root Math element. + /// + /// The root XElement + /// A parsed tree if everything went ok; null otherwise. + public static Math Parse(XElement rootElement) + { + return GenericParse(rootElement) as Math; + } + + + /// + /// Parses the element and creates a parse tree + /// + /// The element to parse from. + /// The root of the parse tree. + public static IBuildable GenericParse(XElement element) + { + // make children recursively depending on the number of contained elements + var elems = element.Elements(); + var c = camel(element.Name.LocalName); + var t = Type.GetType("MathMLToCSharpLib.Entities." + c); + if (t == null) + { + throw new Exception("Type " + c + " has not yet been implemented."); + } + switch (elems.Count()) + { + case 0: // return text + Trace.WriteLine(String.Format(CultureInfo.InvariantCulture, "Creating {0} with 0 params.", t.Name)); + return Activator.CreateInstance(t, element.Value) as IBuildable; + case 1: + Trace.WriteLine(String.Format(CultureInfo.InvariantCulture, "Creating {0} with 1 param.", t.Name)); + var first = GenericParse(elems.First()); + return Activator.CreateInstance(t, first) as IBuildable; + case 2: + var pair = elems.Select(e => GenericParse(e)).ToArray(); + IBuildable result; + // some cases require a binary constructor; other cases requre an N-ary one instead + try + { + result = Activator.CreateInstance(t, pair[0], pair[1]) as IBuildable; + } catch + { + result = Activator.CreateInstance(t, new[] { pair }) as IBuildable; + } + return result; + default: // has a single item + var pars = elems.Select(e => GenericParse(e)).ToArray(); + return Activator.CreateInstance(t, new [] { pars }) as IBuildable; + } + } + } +} diff --git a/MathMLToCSharpLib/PowerCollections.dll b/MathMLToCSharpLib.o/PowerCollections.dll similarity index 100% rename from MathMLToCSharpLib/PowerCollections.dll rename to MathMLToCSharpLib.o/PowerCollections.dll diff --git a/MathMLToCSharpLib/Properties/AssemblyInfo.cs b/MathMLToCSharpLib.o/Properties/AssemblyInfo.cs similarity index 100% rename from MathMLToCSharpLib/Properties/AssemblyInfo.cs rename to MathMLToCSharpLib.o/Properties/AssemblyInfo.cs diff --git a/MathMLToCSharpLib.o/Singleton.cs b/MathMLToCSharpLib.o/Singleton.cs new file mode 100644 index 0000000..75108fe --- /dev/null +++ b/MathMLToCSharpLib.o/Singleton.cs @@ -0,0 +1,24 @@ +using System; + +namespace MathMLToCSharpLib +{ + internal class Singleton + { + private static Singleton instance; + + private Singleton() { } + + public static Singleton Instance + { + get + { + if (instance == null) + { + instance = new Singleton(); + } + return instance; + } + } + public BuildContextOptions Options { get; set; } + } +} diff --git a/MathMLToCSharpLib.o/Util.cs b/MathMLToCSharpLib.o/Util.cs new file mode 100644 index 0000000..db1465f --- /dev/null +++ b/MathMLToCSharpLib.o/Util.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace MathMLToCSharpLib +{ + internal static class Util + { + // overkill data taken from http://www.unicode.org/Public/5.0.0/ucd/UnicodeData.txt + private const string greekChars = +@"0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1; +0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2; +0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3; +0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4; +0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5; +0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6; +0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7; +0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8; +0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9; +039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA; +039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB; +039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC; +039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD; +039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE; +039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF; +03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0; +03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1; +03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3; +03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4; +03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5; +03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6; +03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7; +03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8; +03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9; +03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA; +03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB; +03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386 +03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388 +03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389 +03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A +03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;; +03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391 +03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392 +03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393 +03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394 +03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395 +03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396 +03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397 +03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398 +03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399 +03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A +03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B +03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C +03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D +03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E +03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F +03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0 +03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1 +03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4 +03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5 +03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6 +03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7 +03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8 +03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9"; + + private static readonly Dictionary greek; + + static Util() + { + greek = new Dictionary(); + StringReader sr = new StringReader(greekChars); + string line; + while ((line = sr.ReadLine()) != null) + { + string [] parts = line.Split(';'); + string name = parts[1].ToLower(); + bool isCap = name.Contains("capital"); + name = name.Substring(name.LastIndexOf("letter") + 7); + if (isCap) + name = "" + char.ToUpper(name[0]) + name.Substring(1); + name = name.Replace(' ', '_'); + greek.Add((char) int.Parse(parts[0], NumberStyles.AllowHexSpecifier), + name); + } + } + + internal static string GreekLetterName(char letter) + { + if (greek.ContainsKey(letter)) + return greek[letter]; + return letter.ToString(); + } + + internal static char IntToLetter(int n) + { + return (char)('a' + n); + } + } +} diff --git a/MathMLToCSharpLib/BuildContext.cs b/MathMLToCSharpLib/BuildContext.cs index c597c62..14b5618 100644 --- a/MathMLToCSharpLib/BuildContext.cs +++ b/MathMLToCSharpLib/BuildContext.cs @@ -2,8 +2,9 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; + using MathMLToCSharpLib.Entities; -using Wintellect.PowerCollections; + namespace MathMLToCSharpLib { @@ -13,10 +14,11 @@ namespace MathMLToCSharpLib /// public sealed class BuildContext { + private readonly IList errors = new List(); - private readonly List> sums = new List>(); + private readonly List> sums = new List>(); private readonly IList tokens = new ArrayList(); - private readonly ICollection vars = new OrderedSet(); + private readonly ICollection vars = new SortedSet(); private readonly IList possibleDivisionsByZero = new List(); internal IList PossibleDivisionsByZero { @@ -36,7 +38,7 @@ public BuildContext() : this(new BuildContextOptions()) { } /// Initializes a new instance of the class. /// /// Build options. - internal BuildContext(BuildContextOptions options) + public BuildContext(BuildContextOptions options) { Options = options; } @@ -132,7 +134,7 @@ public bool OnRhs } - public IList> Sums + public IList> Sums { get { @@ -142,7 +144,7 @@ public IList> Sums public bool InMatrixDeterminate { get; set; } //string: function name, bool: go through bracket - public Stack> BuiltinFuncPair = new Stack>(); + public Stack> BuiltinFuncPair = new Stack>(); /// /// Adds a sum. @@ -154,9 +156,9 @@ public void AddSum(ISum sum) { char c1 = c; if (!vars.Contains(c1.ToString()) && - sums.FindIndex(i => i.Second == c1) == -1) + sums.FindIndex(i => i.Item2 == c1) == -1) { - sums.Add(new Pair(sum, c)); + sums.Add(new Tuple(sum, c)); vars.Add(c.ToString()); return; } @@ -167,19 +169,19 @@ public void AddSum(ISum sum) public char GetSumIdentifier(ISum sum) { - int idx = sums.FindIndex(i => i.First == sum); + int idx = sums.FindIndex(i => i.Item1 == sum); if (idx != -1) - return sums[idx].Second; + return sums[idx].Item2; else return '?'; } } - internal class BuildContextSum - { - private IBuildable[] initTokens; - private IBuildable[] limitTokens; - private IBuildable[] statement; - } + //internal class BuildContextSum + //{ + // private IBuildable[] initTokens; + // private IBuildable[] limitTokens; + // private IBuildable[] statement; + //} public enum EquationDataType { diff --git a/MathMLToCSharpLib/Entities/Math.cs b/MathMLToCSharpLib/Entities/Math.cs index 92fa197..5b7eb4b 100644 --- a/MathMLToCSharpLib/Entities/Math.cs +++ b/MathMLToCSharpLib/Entities/Math.cs @@ -18,12 +18,12 @@ public Math(IBuildable content) : base(new[] { content }) { } public Math(IBuildable[] contents) : base(contents) { } #region IXmlSerializable Methods - public XmlSchema GetSchema() + public new XmlSchema GetSchema() { return null; } - public void ReadXml(XmlReader reader) + public new void ReadXml(XmlReader reader) { List tempContent = new List(); while(reader.Read()) @@ -44,7 +44,7 @@ public void ReadXml(XmlReader reader) } - public void WriteXml(XmlWriter writer) + public new void WriteXml(XmlWriter writer) { writer.WriteStartDocument(); writer.WriteStartElement(this.GetType().Name); @@ -70,7 +70,7 @@ public override void Visit(StringBuilder sb, BuildContext context) var builder = new StringBuilder(); foreach (var v in context.Sums) { - builder.AppendLine(v.First.Expression(context)); + builder.AppendLine(v.Item1.Expression(context)); } sb.Insert(0, builder.ToString()); } diff --git a/MathMLToCSharpLib/Entities/Mi.cs b/MathMLToCSharpLib/Entities/Mi.cs index c2473d0..7b898f9 100644 --- a/MathMLToCSharpLib/Entities/Mi.cs +++ b/MathMLToCSharpLib/Entities/Mi.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Reflection; using System.Text; -using Wintellect.PowerCollections; namespace MathMLToCSharpLib.Entities { @@ -36,7 +35,7 @@ public override void Visit(StringBuilder sb, BuildContext bc) //postfix Built-in function detection if (content == "Eigenvectors" || content == "Eigenvalues") { - bc.BuiltinFuncPair.Push(new Pair(content, false)); + bc.BuiltinFuncPair.Push(new Tuple(content, false)); bc.Tokens.Add(this); return; } @@ -60,14 +59,14 @@ public override void Visit(StringBuilder sb, BuildContext bc) string replaceTerm = string.Empty; if (Semantics.rep.ContainsKey(varName)) { - Pair p = Semantics.rep[varName]; - replaceTerm = p.First; - if (string.IsNullOrEmpty(p.Second)) + Tuple p = Semantics.rep[varName]; + replaceTerm = p.Item1; + if (string.IsNullOrEmpty(p.Item2)) needReplace = true; else { Type bct = typeof(BuildContextOptions); - PropertyInfo pi = bct.GetProperty(p.Second); + PropertyInfo pi = bct.GetProperty(p.Item2); Debug.Assert(pi != null); needReplace = (bool)pi.GetValue(bc.Options, null); } diff --git a/MathMLToCSharpLib/Entities/Mo.cs b/MathMLToCSharpLib/Entities/Mo.cs index c6fe6b3..b4d9117 100644 --- a/MathMLToCSharpLib/Entities/Mo.cs +++ b/MathMLToCSharpLib/Entities/Mo.cs @@ -1,4 +1,5 @@ -using System.Collections.Specialized; +using System; +using System.Collections.Specialized; using System.Diagnostics; using System.Text; @@ -133,16 +134,16 @@ public override void Visit(StringBuilder sb, BuildContext bc) } //Built in function - if (content == "(" && bc.BuiltinFuncPair.Count != 0 && bc.BuiltinFuncPair.Peek().Second == false) + if (content == "(" && bc.BuiltinFuncPair.Count != 0 && bc.BuiltinFuncPair.Peek().Item2 == false) { var pr = bc.BuiltinFuncPair.Pop(); - pr.Second = true; + pr = new Tuple(pr.Item1, true); bc.BuiltinFuncPair.Push(pr); return; } - else if (content == ")" && bc.BuiltinFuncPair.Count != 0 && bc.BuiltinFuncPair.Peek().Second == true) + else if (content == ")" && bc.BuiltinFuncPair.Count != 0 && bc.BuiltinFuncPair.Peek().Item2 == true) { - switch (bc.BuiltinFuncPair.Peek().First) + switch (bc.BuiltinFuncPair.Peek().Item1) { default: sb.Append(")"); diff --git a/MathMLToCSharpLib/Entities/Msup.cs b/MathMLToCSharpLib/Entities/Msup.cs index 51969d9..43c7541 100644 --- a/MathMLToCSharpLib/Entities/Msup.cs +++ b/MathMLToCSharpLib/Entities/Msup.cs @@ -1,5 +1,5 @@ +using System; using System.Text; -using Wintellect.PowerCollections; namespace MathMLToCSharpLib.Entities { @@ -11,11 +11,11 @@ public class Msup : WithBinaryContent public Msup() { } public Msup(IBuildable first, IBuildable second) : base(first, second) { } - public Pair Values + public Tuple Values { get { - return new Pair(first, second); + return new Tuple(first, second); } } diff --git a/MathMLToCSharpLib/Entities/Semantics.cs b/MathMLToCSharpLib/Entities/Semantics.cs index 2bd6f40..fe33a9a 100644 --- a/MathMLToCSharpLib/Entities/Semantics.cs +++ b/MathMLToCSharpLib/Entities/Semantics.cs @@ -1,67 +1,67 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Collections.Specialized; -using Wintellect.PowerCollections; namespace MathMLToCSharpLib.Entities { - /// - /// Completely spurious element that MathType 6 insists on generating. - /// - public class Semantics : WithBuildableContents - { - internal const string starPrefix = "_star"; - /// - /// Functions which need replacement. + /// Completely spurious element that MathType 6 insists on generating. /// - internal static readonly List knownFuncts; + public class Semantics : WithBuildableContents + { + internal const string starPrefix = "_star"; - /// - /// Symbols and function names that can be replaced. - /// - internal static readonly Dictionary> rep; - /// - /// Trig functions which have an inverse, and their inverse values. - /// - internal static StringDictionary inverseTrigs; + /// + /// Functions which need replacement. + /// + internal static readonly List knownFuncts; + /// + /// Symbols and function names that can be replaced. + /// + internal static readonly Dictionary> rep; + /// + /// Trig functions which have an inverse, and their inverse values. + /// + internal static StringDictionary inverseTrigs; - static Semantics() - { - rep = new Dictionary> + + static Semantics() + { + rep = new Dictionary> { - {"∞", new Pair("double.MaxValue", null)}, - {"π", new Pair("Math.PI", "ReplacePiWithMathPI")}, - {"e", new Pair("Math.E", "ReplaceEWithMathE")}, - {"exp", new Pair("Math.Exp", "ReplaceExpWithMathExp")}, - {"*", new Pair("star", null)}, + {"∞", new Tuple("double.MaxValue", null)}, + {"π", new Tuple("Math.PI", "ReplacePiWithMathPI")}, + {"e", new Tuple("Math.E", "ReplaceEWithMathE")}, + {"exp", new Tuple("Math.Exp", "ReplaceExpWithMathExp")}, + {"*", new Tuple("star", null)}, // functions - {"cos", new Pair("Math.Cos(", null)}, - {"cosh", new Pair("Math.Cosh(", null)}, - {"sec", new Pair("1/Math.Cos(", null)}, - {"sech", new Pair("1/Math.Cosh(", null)}, - {"sin", new Pair("Math.Sin(", null)}, - {"sinh", new Pair("Math.Sinh(", null)}, - {"csc", new Pair("1/Math.Sin(", null)}, - {"csch", new Pair("1/Math.Sinh(", null)}, - {"tan", new Pair("Math.Tan(", null)}, - {"tanh", new Pair("Math.Tanh(", null)}, - {"cot", new Pair("1/Math.Tan(", null)}, - {"coth", new Pair("1/Math.Tanh(", null)}, - {"ln", new Pair("Math.Log(", null)}, - {"log", new Pair("Math.Log10(", null)}, + {"cos", new Tuple("Math.Cos(", null)}, + {"cosh", new Tuple("Math.Cosh(", null)}, + {"sec", new Tuple("1/Math.Cos(", null)}, + {"sech", new Tuple("1/Math.Cosh(", null)}, + {"sin", new Tuple("Math.Sin(", null)}, + {"sinh", new Tuple("Math.Sinh(", null)}, + {"csc", new Tuple("1/Math.Sin(", null)}, + {"csch", new Tuple("1/Math.Sinh(", null)}, + {"tan", new Tuple("Math.Tan(", null)}, + {"tanh", new Tuple("Math.Tanh(", null)}, + {"cot", new Tuple("1/Math.Tan(", null)}, + {"coth", new Tuple("1/Math.Tanh(", null)}, + {"ln", new Tuple("Math.Log(", null)}, + {"log", new Tuple("Math.Log10(", null)}, // inverse trig functions - {"arcsin", new Pair("Math.Asin(", null)}, - {"arccos", new Pair("Math.Acos(", null)}, - {"arctan", new Pair("Math.Atan(", null)}, - {"arccsc", new Pair("Math.Asin(1/", null)}, - {"arcsec", new Pair("Math.Acos(1/", null)}, - {"arccot", new Pair("Math.Atan(1/", null)} + {"arcsin", new Tuple("Math.Asin(", null)}, + {"arccos", new Tuple("Math.Acos(", null)}, + {"arctan", new Tuple("Math.Atan(", null)}, + {"arccsc", new Tuple("Math.Asin(1/", null)}, + {"arcsec", new Tuple("Math.Acos(1/", null)}, + {"arccot", new Tuple("Math.Atan(1/", null)} }; - inverseTrigs = new StringDictionary + inverseTrigs = new StringDictionary { {"sin", "arcsin"}, {"cos", "arccos"}, @@ -71,7 +71,7 @@ static Semantics() {"cot", "arccot"} }; - knownFuncts = new List + knownFuncts = new List { "cos", "cosh", @@ -95,21 +95,21 @@ static Semantics() "log", "exp" // exponent, should be user-selectable }; - } + } - public Semantics(IBuildable[] contents) : base(contents) {} + public Semantics(IBuildable[] contents) : base(contents) { } - internal static string postfixForDataType(EquationDataType type) - { - switch (type) - { - case EquationDataType.Decimal: - return "M"; - case EquationDataType.Float: - return "f"; - default: - return string.Empty; - } + internal static string postfixForDataType(EquationDataType type) + { + switch (type) + { + case EquationDataType.Decimal: + return "M"; + case EquationDataType.Float: + return "f"; + default: + return string.Empty; + } + } } - } } \ No newline at end of file diff --git a/MathMLToCSharpLib/Entities/WithBinaryContent.cs b/MathMLToCSharpLib/Entities/WithBinaryContent.cs index d88f417..99026ef 100644 --- a/MathMLToCSharpLib/Entities/WithBinaryContent.cs +++ b/MathMLToCSharpLib/Entities/WithBinaryContent.cs @@ -2,7 +2,6 @@ using System.Text; using System.Xml; using System.Xml.Schema; -using Wintellect.PowerCollections; namespace MathMLToCSharpLib.Entities { @@ -17,11 +16,11 @@ protected WithBinaryContent(IBuildable first, IBuildable second) this.second = second; } - public Pair Contents + public Tuple Contents { get { - return new Pair(first, second); + return new Tuple(first, second); } } diff --git a/MathMLToCSharpLib/Entities/WithBuildableContents.cs b/MathMLToCSharpLib/Entities/WithBuildableContents.cs index adfc9fc..33ff2ae 100644 --- a/MathMLToCSharpLib/Entities/WithBuildableContents.cs +++ b/MathMLToCSharpLib/Entities/WithBuildableContents.cs @@ -3,9 +3,7 @@ using System.Diagnostics; using System.Text; using System.Xml; -using System.Xml.Linq; using System.Xml.Schema; -using Wintellect.PowerCollections; namespace MathMLToCSharpLib.Entities { @@ -141,7 +139,7 @@ public XmlSchema GetSchema() public void ReadXml(XmlReader reader) { List tempContent = new List(); - while(reader.Read()) + while (reader.Read()) { if (reader.IsStartElement()) { @@ -184,10 +182,10 @@ private static void ReplaceInverseTrigFunctions(IBuildable[] contents) if (c is Msup) { bool funcIsTrig = false, radixIsNeg1 = false; - Pair terms = (c as Msup).Values; - if (terms.First is Mrow) + Tuple terms = (c as Msup).Values; + if (terms.Item1 is Mrow) { - Mrow row1 = terms.First as Mrow; + Mrow row1 = terms.Item1 as Mrow; if (row1.Contents.Length > 0 && row1.Contents[0] is Mi) { if (Semantics.inverseTrigs.ContainsKey((row1.Contents[0] as Mi).Content)) @@ -197,9 +195,9 @@ private static void ReplaceInverseTrigFunctions(IBuildable[] contents) } } } - if (terms.Second is Mrow) + if (terms.Item2 is Mrow) { - Mrow row2 = terms.Second as Mrow; + Mrow row2 = terms.Item2 as Mrow; StringBuilder sb = new StringBuilder(); BuildContext bc = new BuildContext(); row2.Visit(sb, bc); diff --git a/MathMLToCSharpLib/MathMLToCSharpLib.csproj b/MathMLToCSharpLib/MathMLToCSharpLib.csproj index 9a1c788..f6726d1 100644 --- a/MathMLToCSharpLib/MathMLToCSharpLib.csproj +++ b/MathMLToCSharpLib/MathMLToCSharpLib.csproj @@ -1,83 +1,27 @@ - - - + + - Debug - AnyCPU - {3909E9A8-4E8A-4358-A8A9-3BA634090378} - Library - Properties - MathMLToCSharpLib - MathMLToCSharpLib - v3.5 - 512 - + netcoreapp2.2;net35;net4;net45;net47 + Copyright © 2017 - 2019 Dmitri Nesteruk, PhilipXYC, Smurf-IV + 1.1.0 + https://github.com/philipxyc/MathMLToCSharp + https://github.com/philipxyc/MathMLToCSharp + GitHub + expression-parser; csharp; netcore2; mathml-parser; mathml; + There were plenty of problems in converting from XML to C#, but the main idea stayed the same: correctly implement the Visitor pattern over each possible MathML element, removing unnecessary information and supplying that information which is missing. - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 + + + false + - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + + + 1701;1702;0169; + - - .\PowerCollections.dll - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + diff --git a/MathMLToCSharpLib/MathMLToCSharpLib.nuspec b/MathMLToCSharpLib/MathMLToCSharpLib.nuspec index 05c7c72..71b0571 100644 --- a/MathMLToCSharpLib/MathMLToCSharpLib.nuspec +++ b/MathMLToCSharpLib/MathMLToCSharpLib.nuspec @@ -11,6 +11,6 @@ MathML to C# expression Converter Added xml serialization support Copyright 2018 - Math MathML Conversion Caculate + Math MathML Conversion Calculate \ No newline at end of file diff --git a/MathMLToCSharpLib/PreNet40.cs b/MathMLToCSharpLib/PreNet40.cs new file mode 100644 index 0000000..46a1277 --- /dev/null +++ b/MathMLToCSharpLib/PreNet40.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace MathMLToCSharpLib +{ +#if NET35 + public static class Tuple + { + public static Tuple Create(T1 item1, T2 item2) + { + return new Tuple(item1, item2); + } + } + + [DebuggerDisplay("Item1={Item1};Item2={Item2}")] + public class Tuple : IFormattable + { + public T1 Item1 { get; private set; } + public T2 Item2 { get; private set; } + + public Tuple(T1 item1, T2 item2) + { + Item1 = item1; + Item2 = item2; + } + + #region Optional - If you need to use in dictionaries or check equality + private static readonly IEqualityComparer Item1Comparer = EqualityComparer.Default; + private static readonly IEqualityComparer Item2Comparer = EqualityComparer.Default; + + public override int GetHashCode() + { + var hc = 0; + if (!object.ReferenceEquals(Item1, null)) + hc = Item1Comparer.GetHashCode(Item1); + if (!object.ReferenceEquals(Item2, null)) + hc = (hc << 3) ^ Item2Comparer.GetHashCode(Item2); + return hc; + } + public override bool Equals(object obj) + { + var other = obj as Tuple; + if (object.ReferenceEquals(other, null)) + return false; + else + return Item1Comparer.Equals(Item1, other.Item1) && Item2Comparer.Equals(Item2, other.Item2); + } + #endregion + + #region Optional - If you need to do string-based formatting + public override string ToString() { return ToString(null, CultureInfo.CurrentCulture); } + public string ToString(string format, IFormatProvider formatProvider) + { + return string.Format(formatProvider, format ?? "{0},{1}", Item1, Item2); + } + #endregion + } + + public class SortedSet : SortedList, ICollection + { + public SortedSet() : base() + { + } + + public T Min + { + get + { + if ((base.Count) >= 1) + { + return base.Keys[0]; + } + else + { + return default(T); + } + } + } + + public T Max + { + get + { + if ((base.Count) >= 1) + { + return base.Keys[base.Keys.Count - 1]; + } + else + { + return default(T); + } + } + } + + + public bool Contains(T value) + { + return base.ContainsKey(value); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool IsReadOnly { get; } + + public void Add(T value) + { + base.Add(value, 0); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Keys.GetEnumerator(); + } + } +#endif +} diff --git a/Tests/ConversionTests.cs b/Tests/ConversionTests.cs index d008a5a..c2ee592 100644 --- a/Tests/ConversionTests.cs +++ b/Tests/ConversionTests.cs @@ -1,70 +1,72 @@ -using MathMLToCSharp.Entities; -using NUnit.Framework; -using MathMLToCSharp; +using System; using System.Text; using System.Xml.Linq; -using System; + +using MathMLToCSharpLib; +using MathMLToCSharpLib.Entities; + +using NUnit.Framework; namespace Tests { - [TestFixture] - public class ConversionTests - { - private static void Main() { } - - private static string ParseAndOutput(string mathML) + [TestFixture] + public class ConversionTests { - return ParseAndOutput(mathML, new BuildContextOptions()); - } + private static void Main() { } - private static string ParseAndOutput(string mathML, BuildContextOptions options) - { - IBuildable b = Parser.Parse(XElement.Parse(mathML)); - StringBuilder sb = new StringBuilder(); - b.Visit(sb, new BuildContext(options)); - return sb.ToString(); - } + private static string ParseAndOutput(string mathML) + { + return ParseAndOutput(mathML, new BuildContextOptions()); + } - [Test] - public void EmptyMathTest() - { - Assert.AreEqual( - "", - ParseAndOutput(""), - "Empty math element must render to nothing."); - } + private static string ParseAndOutput(string mathML, BuildContextOptions options) + { + IBuildable b = Parser.Parse(XElement.Parse(mathML)); + StringBuilder sb = new StringBuilder(); + b.Visit(sb, new BuildContext(options)); + return sb.ToString(); + } - [Test] - public void MathWithNumberTest() - { - Assert.AreEqual("2;", - ParseAndOutput("2"), - "Basic mn block must render properly."); - } + [Test] + public void EmptyMathTest() + { + Assert.AreEqual( + "", + ParseAndOutput(""), + "Empty math element must render to nothing."); + } - [Test] - public void BasicAssignmentTest() - { - Assert.AreEqual( - "double n = 0.0;" + Environment.NewLine + "n=2;", - ParseAndOutput("n=2"), - "Declaration and assignment ought to be together."); - } + [Test] + public void MathWithNumberTest() + { + Assert.AreEqual("2;", + ParseAndOutput("2"), + "Basic mn block must render properly."); + } - [Test] - public void BasicSubscriptTest() - { - Assert.AreEqual( - "double a_b = 0.0;" + Environment.NewLine + "a_b;", - ParseAndOutput("ab")); - } + [Test] + public void BasicAssignmentTest() + { + Assert.AreEqual( + "double n = 0.0;" + Environment.NewLine + "n=2;", + ParseAndOutput("n=2"), + "Declaration and assignment ought to be together."); + } - [Test] - public void DoubleSubscriptTest() - { - Assert.AreEqual( - "double a_b_c = 0.0;" + Environment.NewLine + "a_b_c;", - ParseAndOutput(@" + [Test] + public void BasicSubscriptTest() + { + Assert.AreEqual( + "double a_b = 0.0;" + Environment.NewLine + "a_b;", + ParseAndOutput("ab")); + } + + [Test] + public void DoubleSubscriptTest() + { + Assert.AreEqual( + "double a_b_c = 0.0;" + Environment.NewLine + "a_b_c;", + ParseAndOutput(@" a @@ -80,6 +82,6 @@ public void DoubleSubscriptTest() ")); + } } - } } diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 0bbfcb6..bbfb7d8 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -69,13 +69,9 @@ - {3909e9a8-4e8a-4358-a8a9-3ba634090378} + {7e67aafd-564c-4362-b93f-7fdb912e2df7} MathMLToCSharpLib - - {9c1737f0-6dcc-4cc5-8c8e-f9b4073683f8} - MathMLToCSharp - + + c + + 2 + + +"; + var result = Utils.ParseAndOutput(input, new BuildContextOptions() + { + InitializeVariables = false, + MaxInlinePower = 1 + }); + Assert.AreEqual(@"E=m*Math.Pow(c, 2);", result); + } + + [Test] + public void A030_MathML_Einstein_Mass_Equation() + { + const string input = @" + + E + + n + e + r + g + y + + + = + + M + + a + s + s + + + + + c + + l + i + g + h + t + S + p + e + e + d + + + 2 + + +" + ; + + var result = Utils.ParseAndOutput(input, new BuildContextOptions() + { + InitializeVariables = false, + MaxInlinePower = 1 + }); + Assert.AreEqual(@"E_nergy=M_ass*Math.Pow(c_lightSpeed, 2);", result); + } + + + [Test] + public void A040_CreateGrouped_Variables() + { + // Note: Still waiting for a fix for https://github.com/verybadcat/CSharpMath/issues/59 + const string input = + @" + + Gap + + 2 + + +" + ; + var result = Utils.ParseAndOutput(input, new BuildContextOptions() + { + InitializeVariables = false, + MaxInlinePower = 1 + }); + Assert.AreEqual(@"Math.Pow(Gap, 2);", result); + } + + [Test] + public void A041_CreateGrouped_Variables() + { + // Note: Still waiting for a fix for https://github.com/verybadcat/CSharpMath/issues/59 + const string input = + @" + + Gap + + 2 + + + gb +" + ; + var result = Utils.ParseAndOutput(input, new BuildContextOptions() + { + InitializeVariables = false, + MaxInlinePower = 1 + }); + Assert.AreEqual(@"Math.Pow(Gap, 2)*gb;", result); + } + + + /// + [Test] + public void A045_CreateGrouped_Variable() + { + // Note: Still waiting for a fix for https://github.com/verybadcat/CSharpMath/issues/59 + const string input = + @" + + F + + n + + + = + + Gap + + 2 + + +" + ; + var result = Utils.ParseAndOutput(input, new BuildContextOptions() + { + InitializeVariables = false, + MaxInlinePower = 1 + }); + Assert.AreEqual(@"F_n=Math.Pow(Gap, 2);", result); + } + + + + } +} diff --git a/Tests/ConversionTests.cs b/Tests/ConversionTests.cs index c2ee592..34bec11 100644 --- a/Tests/ConversionTests.cs +++ b/Tests/ConversionTests.cs @@ -66,22 +66,17 @@ public void DoubleSubscriptTest() { Assert.AreEqual( "double a_b_c = 0.0;" + Environment.NewLine + "a_b_c;", - ParseAndOutput(@" - - - a - - - b - c - - - - - - - -")); + ParseAndOutput("abc")); } + + [Test] + public void DoubleMultiSubscriptTest() + { + // LaTeX == a_{boost_{charge}} + Assert.AreEqual( + "double a_boost_charge = 0.0;" + Environment.NewLine + "a_boost_charge;", + ParseAndOutput("aboostcharge")); + } + } } diff --git a/Tests/SimpleEquations.cs b/Tests/SimpleEquations.cs new file mode 100644 index 0000000..167a1e3 --- /dev/null +++ b/Tests/SimpleEquations.cs @@ -0,0 +1,66 @@ +using NUnit.Framework; + +namespace MathMLToCSharpLib.Tests +{ + public class SimpleEquations + { + /// + /// https://en.wikipedia.org/wiki/Froude_number + /// + [Test] + public void Modified_FroudeNumber_Equation() + { + const string input = + @" + + F + + n + + + = + + + 0 + , + 5 + 1 + 5 + * + V + + m + + + + + + + g + + + L + + W + L + + + + + + +" + ; + + var result = Utils.ParseAndOutput(input, new BuildContextOptions() + { + InitializeVariables = false, + MaxInlinePower = 1 + }); + Assert.AreEqual(@"F_n=((0.515*V_m) / (Math.Sqrt(g*L_WL)));", result); + } + + + + } +} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index bbfb7d8..54ba917 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -22,6 +23,8 @@ 3.5 + + true @@ -41,7 +44,9 @@ 4 - + + ..\packages\NUnit.3.12.0\lib\net35\nunit.framework.dll + 3.5 @@ -59,13 +64,13 @@ + - - - + + @@ -73,7 +78,16 @@ MathMLToCSharpLib + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + l^G&QFC~O*v@E zN5ocxNb*tfqCwOP9us*dktF5td*^X6z=xg?BN^#bo)nD+Q7ZezRX)j|i`_oSr^Fw8 zlD`!06v|--W|`NWr$w1TGD7HhjWBD!1vJN?&q9ZtXGDWRMd80Y&x*|kjRty7TxU=X z(67XO2IT<#T0CP=FQDIuLk5iidR}~P(Bkl?&I_V&DqAL&0lg?H3`zq1R#X{uPguwU z;yVVt2=qJg5+ka!O7W6tW~Aqim&C!exVN=lm`eMSaHr|~&JAbFmqb^Cc4TME-;3i7 zx*TzT5OWOLgSeMPvq9G*?iI1cp!*T`s<^|TH-p*oHL=g2ClU8Y@j4@I=RtAU#66Ge z%?E`$oovx@uZwJpy35x^nMRn=`^Z0uUo+Yf{%3fgd_%l#5S8c+;m+U^g`~cNIYs#KS=&V{MMp6`H6VlqSf*<@qs~ubDHEoMQA41 zpctI9PJSs0EZQW6)7zktkaV087Oj)6GuEO_GU(J8bYjkVGUP0`=zB8iBrMt{vz>Dd zN=A0b9Onv)cF8>F28(vfj?RxP`hhHP9<%5kS>!xp(Oy~NykgLVoE@^W^LLAO$*#_q z7VVbZom{$#3{N{D=LfQf)5oHFWG`o^MSEo*XRJXrIgiS|PK`zTWq)U}MbF8B4*lb4 zlx8g?2Rmn5v|kQ&wpjF>Jj%JnpcY6T>)dD2etDepxJA#&w%=wmt0S#8mma-nmUMXoy8 zx!R%}b*i(+qC&OAx!Gs)cwu^iwf0Vr{1D2>LKT1i+Za^ovSPwsD9?$ zX3P;ugy(y9UZsBHbh2oddeP};(Qb9XnPkzO>JQEw zi|$jeI&}u!iu(McbB0A1syCd?7R{G$IaeC=Z;*W3xx=Ch)nR9^Mf2tR&MytxoAZ+V zzf%%U9pQ&?EKH0PSDwUL-EZU{k%6=9-sMg7m7CouXk`pX?PMt018FX&=fI3I6wCJEZ zU$$8Ewz^1OWYGSc_tnMn8H;wyE%J93O_P_%w+(t4l9$Me?`S)p&e<(5lgC;#O>UKw z4SGK3-_=%`FzDr+FVr@9wneUch1_CMw!1_A!lK1;r+m$#I(fbP$fDKq1{qzfb3cgw za--~L5j>wf$|86^S!I!{ejrb?C`a8X*H~1j?&6ze)L#ywzuY5t+PIJ9{qp-3eJS_K z2P}f;lP_8X&nI8E2%b;=-6D8CnR|+7H9VgzwFsV1R#>!KJu4?$bf@}_oNLj2>P5NS zqD$2Qd9FoQsXxfeEZU`Bl{Z@Sp!%b{$D$|I8}d<$o>OnhUmNs3>ho=R*rM0vVfm>= zZ_D>(^i*Aj_ukv|Dvjxuz=G zm#Xe+vPD;^UTUsIyHsDb+@c3nf3?n{CsnyR-=gQ#V6}sh-VqH|yBO&e{804+i`F_r z)z2-OC5EZrThzlHu0FJ=pF2Y3)e8Jw5U$IuaF0l~v-81&uTDdJc))u3y! z*gRG(Fz8OijZ{qrJ&w4M>MVnPi@4*|c7xtT+;QqggZ_~_%pIj3Fi3Sc7HFSA`5mT+ z(dre0x^)i6vz94nez#F)GKPWQS?)Sk>L4xo)K@x9Ajif~qv=(hl`* zwVGv7+&xL1V$oW6syfr4n;%|BF@ttwKkV*Q$62(`y-rQE=o$A0wb-ENk^3#G z(V}hc9<{-uJ??Gla)S;+@(y*2McdqeQ+HXk$Guzq%%Egst@~s3f<fHy` z`vy(OS?fNe0(H7X6LRA2BP!pbdiOEalaU^Ok1LYH$xz_k4hLL|a{h*%{J0umCEq|? z6{F4JlDv1^C(`tuP@6e!e}wd&P&YB!fj5i(9g=rgv|2u?9x`ZD-YIIodd8wN)KltJ zi{{H`)O!}aEuT|gSoFF3YZX~eR__p_@>JjjRb)|C;CHIGMFoME)ewUwPvGxKMO57bbDwgCNIon+8; z`G>`aYLP`l0v{>5IE_@q-H5~5!=QcnM+ZJuw^%ef@TvN#L9gal20m8@8EqCH=J#{I zP{*&-scer<3VfwD7?jm-*H7C&wYu}1)`y2JkZg-qk-afh}Mql0!8j=YY1%@S9P2zi`^{--PUnq zptIZ5NO9Z5Lmkfxl)8r)Z5O}pcyXY+J2lR6eCFT7eVh^1=Pc2~eUXtq?JUfXqM>XzGqRT=<9xB(JUbMbheuH`neRwD9$;(;IcppomWr^ zRH!Azl?89O{XNORl?7J>23WK`+Tc|9(9wZGK6Gthh(*O&HwT7WbSuhmtVKTt8fnqK zg2UoCi++c=Q5L<4xX~8KU5V1nDVS)=;GeSyjDR)Zeu_*`JB+qZ?{^cbsgJ2TR2lp1%C zjXMyiaVImP{&HB%a_`b{++Sw9zc6TL;VXgJ?m??JOU!mZu&7eZaq%$Ll1{Tk-2J0L*K~R((Byt(&@G+b51j5couwsz)aj!@!aZ!zgPlGNB;EXt zI_}qkCs}k@taTSybadbh_XdNuKytnNutkT(neNL5z1FERu)+PD zk#6a;+})cfKfOLX%l)ZAv_9M9K4s9k;Xts}ec7Vi;AZ!2i+0Hi-A@g=9FpI4v(DCO zULMX3UgDNmv`cPv2N-lcBrkVITa+8z;ZC+_m%PSZXwZ9|0>NwDCW~@|H@F)u+9hvx zFEvOO1%kWX8!gHW-sawE(Jpzr`v@c5=kIj)o49Dv7wS&;1&dtwNA5w3vfX>!_bgg0 z?{`(JE?=~$PVRL(TeMm} zb9W>o-LF4)YZ&cwMix=rY=g!ynwO?`N*Y?8h8ok*+BDRvQFwOI6y$KVK}(Bf0p0AA z{KCD%C;5fD&nNkX`rOS z3vo3*NfB7-ixYvfOdOSn?(@^%{=nHq3xfJ~e{GO%a^sUrpd*-WXO0eQ`n_zr<4eqdU)6`Vw=#SOmHC$31ev;*% zXG(lWyvpH!XR6D1OlRURuvCigaCo(k>J+q$mTz0-NLy|vX!wV=e!QM^IUZQ+W0 z*&gCZ(>ni|Fy-P;U-u$E$ICHO^2q$hx=>BFmH#dH0ik25_N4d|OWwt8t*O7>bPasK zGJgIy;k2z!T=6qjIe2}G!u~YB2`2z8iTk_M7H@*O;vLWc?(b%%rgLvgQ@zXUX{okT zLXYMDZ#a&i($Rh8%(Cclbfnht`xAfIU$;l%x2>Ol&GCB!9rnxneZrB#+E@5})B*Me z+9#x_?*ESP`wg9!meJv?uGAlLK=E&Q`NADI+!<8i<}}5oI6Ca-99ib*HtD1|j-arn z_*ce#d9{@p(}vA9fXceZ;RA=@5rmW)Ob(V&E)%A+#mi{>)YD!*ML9#ZQpRj%{+eZVoG6&cU)GE5&z2D*Sa?+Cpk^f3MRg3Ep1*o5)ZK{&`dKS%-JZ;qP_r@6`M~ zAw8^9rWEwar#DBZI4;Aj{;SEX6uR**%E!8nM%hI<5@D(W+m3L{Dn;Q8QK>e$*Q)#_a>IPovDuX z*XL*8r=NG}lYGAosjydvehyx?hWCqr21FsKBf2rwr?@2Pi2lsctuvIvx@JZotkTL} z&9PH_GP?GNFU4$@U&^t58`gm13bMrO54v5>Wj@*F;5!JE!WK}-F#Rs`XJQufMx3sn z|L!``wV`X~U)Ra4Y@x0b{oLdZ=KLE|9lndhsaEr9K=)hC*SYBW@z1&bu)jX3hj{)l zlkcUk)69(hzeS5m_usQm(I@2on)iqQZ>`V&H_Ju6*PCnF&L(~jnNvWLnocUB@H5?y z7QYsc3%W)26w}`@#oLSu`kwuO5`(c9nR$zY!g?Mc8z?1aIeVSp#33vMQH_Ot|)yuG!C)9fz3ZDJs}d2 zZ+Dp#q0-F*{dt#_5o*C%_q=S@QdlVc?^u@waD= zL-@T&XKul2LG?+oxzTFY?Fl|^ErON>i1rT*V95c_$6cuh2D-iB5}gJ6=}R^PV8abj z;v5JN-4k`>&~A4`*Q0$e17}IefdEPFk3Pk8Cg(C!)^&S6I>2e@_IuEc-QESgwA;tg zg_7=C*2>>@J0^?rJwB^ijOt$DlKl8Aq9sBgwp8^dVcnJ&>OOsGUq&|w}9@=euBeqFjY}X ztvA#0OdsodvbrMDr~6WMr#QrPcMl3LD#qrlL3T%b1R3 zx~w?NDEYs7Nwlcku>Arp>d8j|j41@|gCnUe4vnO+ zJBnzZ(L}3`C%WteqMOGM-F+g_ePf9p8b{O}Pqa@J(W(hVmrW$Pc@ojxlZo!DCVJ>3 zqV80peWnqunojiKbjp3%3<__qA-ZfP(S1zIW^q56&9O|E&7tsVa}(%|r_DuKvRmg~ zjBqQ$Q?fVDrP$r`IEDE{4=o_-E+X3JWTI8yA-Zfa(aon2-F+(2eWwvUR7=!dO0>^1 zqE&UwsVBOR>E=}w-rPX6&l;jtjZB+}9%AYyI5x@rW}?elh;Cj>^bphCXK?H~qV6W9 z=M!DVboa#+?(;q7Y$dvR8`0gD6WwvrvxBI6717OCGv^wj`*spNbS+W$dZK-H z5eL)DSu&baCkF&~>5PLcatMCV2CiSCU)9sOhUZ_zKJGqX<3TA8&W zYjf6?tShsw$+|V`nJj!dfL84I;%orl7R$res`BxzsgCedh45~j@KvbJ_!d+be66Vr z-)QQIbwzJ{JE;#oRns5eLW+rT_!iYvNKeCer)u!EshQB4MgKy8s0>X6oyK$y(~V5~ zM2J6t>0qWKn2uyRhUtXJb&#oMI+bY+)45Eajr)O{siesd=+#6=mE5r z6u(1TNqmuH2PIMak=ZN>1N9~k_v5|wX@C`xF#^3SN;>%hZ>c)u1 zmd5(#!OIi1YwFI3Csq$$*3clv#~YK)iQ4+cW-)y(MNhQRsi>rw+nB6tp0Q+A-O^?; zVNFx>`c$OG`lHl7?(CK&NEjcg_Vb!+6V1u__021N%n6OlGFh|gR@ANYiKK#+E%gn{ z>Jnl`6D)6NfI0Q?##FSAIj6RvVZCQ-JCZ2u=`D5D&2?*%?dpTsjB05=a>D5?^=oSz z>KdEd)upo6`J*6T+u#$eNz^T`_m^;b+%G<^p&`?-+3_`PjGZ1QDPOX)(X!+G7SCDV zROgGIu&#D#a|W*(?O)rtv~I@ojEHf`Wc`Z9+9eHj6BBWNF5}~CnrajE$+U`}l&Gt1 zu1m~WS?g~OA9qF~vl6D(C6gKQ9;>bJ^p-VsiTb5U>adyZQj>@`)g{vUWMzG0ZDRcl zuxqJ@h;fa}eDyFR;RDslHa&m#dgQTga$N)TlcM@eF{f@_vzS_+)MQ$16C%Xq+9drq zJ+52R>zb%H7?YW%n1<5UuBe+<+q`t8n6R!1UAJ!8gmp{nn#jO$%@`|7TAJ&8J=i2v zt$&f>I2uuC!4!kD;_`VRo1)NCh@4nk-_Vk%GX{w9Xr~qN#QId0)s0IVT9(y8m3~tE znuglu}F-)TK6ZL3(F)g`bZgYJ@Qg|(r5}b|kkf}_ynx266BBtQB z0J#QFyGZm!(w6^-!(8LVqhOu{GbYa5V9Gk+O>PF=Ejd~LE$fHSu#&4{^86YJNd zmtl4bj86&8sjE#?#n13H2_@%`Pd2wngG3B7BTrvU+mv-VQfZ@KG}JGx_vKbqmt2~t z&*&r7y8oogzb3gfo@l6FB4)2o!X*gLQ#ZD(5&qP@`qE;9t7iyZVJlb2Ja~M_f{sNsC z4pFSAYd%$10k;;x>GAQ%j$_D^*-S{Sz7eB?d=FA%i%sX56Bj(J>XMr6t20EgNUC#aB|aIFh^6p%t^G=abRL?13ETW zRqb^C<@uOHdQKDUXi-c(9e7PZo?LhiR`@)#Wg4#w&2i1s;&668A%EP|x>`iibiriC za*;lXkn5>WO&!){!w0Teg0@4U%`!lElRp`0+d4J7!ju%>cz%p^T~gp>YK| z*7$e}Rx-LHdA|%XrSdP$qb9zwPRy!H#v9PZxth6(b@fh1)#_?BRZ5*UrIR84jb$TfceJ)wo2Wc0JEk%eCXLiDPw_G$TD(Rlj_B zUE3ZgTqDT+=&w>%sO>;^`Z%)sy^A& zP`h5+QU}+OG%ib*Sa2CMHJnsthM~>%tmcaHiljkReFAeVCc5gz<#FVwQGK1!Hzw$z z>o+77f#SClk+S4pMpB(5;a+CfpNZ0$M9oypEg~k>HFFi|<(2LIOW=Ne%afYgWD*Pb z`sMXli_D2v(zNEe5gvjK%`>Kmy3;{cP^c+Uw+!wJD~al)Sv0X&b#h`u9E+jG6`Z?? zf)J*yx@Bp+ahO_Z!Ai#!^=t5Jyskh8@vmahb5G51aE(}j!i0DemyAQSGMlgtYp0}W zs9WAF67?%qHjAow9ac`X=%v-12iY_53}NEoAN)`cC#=6j9LZpf*MI?l5f7(ALxcwn z!dTEG>egU&u9pkiDR7aJ81m@;)a4AWPV%@%J+Na%rNx`s9Z>aQxZ?-TfZ2LbS0^*v z8^^WbWw=BZ%HW}%k(sV)gvni_&l8V>#{05+7SwahM1;!H=_c1oOC9?k~ga&$Vy$S&BMGI)_@wayXAP z8Rlpiy*T#r&{F9X}TT+PPCL6m+&kI}ISjQr^Yl4}$J)8V_rBe1% z^7e0NOs~n4dVfW(oqTvww=?%JAIr=(h(xb2J519hd<^bOb_%Rc+90hpxhXs|z2Djd z{=FzO9e6yd2IG%BRtD6)%#0Hayuo1*4G3?}k`~7jv)s4&Q)={FhH07F!&&TGKr`L0))?>xsTZ=L_(}E|X zou*;>maN|N;mO%zdJ(89tkvU<(`pl|F&cR*>M!N2x~4exq$Xu;KTo^lT<6&JyO%Q+ z$0tNH6;}lcT#vareZt~e5Y@cmFbPn2LIZo?q?m{uWUVn-J(=gC>Vq=e% zAD4hP$7G~SgB@SvvT5~6s4rbQFsavDTX-|x z+tLh%a|<3LX4fmo1$bc^6Bzik4Q9RvXuJAVdxSdgn&Ucb)NFUBC|ey{EbrK*mQw|)`?*ymD!r*mVw#i@E? zVPEw4m9+`xRiV4hfq!Gbmh*%`NmSJ>uf-{cSd-TLdeqaP((PmtWj3eEp{jTb?HxGG zOEn$z)?hYQE$rE#6RAzqHFG!8Ge>O$Z&}H+(sHpn$y1a$b>edry(ZSyrmYK8&R{m3 zoe1yDLM%%iMDXzOZL)bx__05%=R^zIb}yd#=~I76T3t-%2ZpMaPii_-~S z4ImM8jCIZ2`OI02m=m8GKZ6(Fm=&=#H>Q)^U@Sdb8fp_5$nYuD$g~c}YIFu4Y$e4K zbF5z($6QoP+W{U6N$(i~b`JkfduJCL*KytPxp$YlcXzed+AE~Bnq_n8lrag53`NV9 z?TV>sTC!<g=X*T)Q-Iv;+1A=MAS+_%^C0ZbwcFQD+e4RSkzZ?xNqpnsL*`Jtw9Tz-Q~9pp z+2oqPodcs(*1V|tImSYAbM`*bV!L$_?e1DRYXEW6FC$R+HKzM~E$bhoRpVui&QBw( z3;M$q8``J3sjV7!4v&b7J!gYG@0E$`!H1o0vV;NAiIy0sbf$=8vPa>Cj;c~;p_8W> zA_JhE^If>GOepOY?MhzNuDztvx0jYbPqVv)FS6Ou7zg{8pzzpAi`6>A$qU`=(o$|t zvSjw2*4Ut1YOlDXOUtPB6YHH@re~Xc-dVtdHS*=KoGgYm(wS8sk#J{Pg#1jswwkxr zvMuCR#(`Gm0_b$3#e(p?GA>;zuEZc7=6hmh7DT29j3*|C%|GHb&Mi1k&X9-w7zt+|^h9qI0%4SF_T;5zw zb~hIn-E3uXlI@6<)+Ly`qJR%mxyu(jeJ(BAYuXI@5D{W4Sx;j2jNq1SE5npuQ+(H< z6)bo$8#caJvwCl0Vh3Ph7IxQtgoM31+s2>tVU7F&Dw_tajf`e~?IvM3ri-k=&3hX` zZEh~hf>+u%8O3V0fObqb9UibSY1`7jj;e`U*@|;DUrJ&suB#ndsbIEPG{JOXu&w{a z6R!@L^H{V9ftpZ~KLdD=eG>M;MwMlwbGGEM0d%)kPc3tAOYX8;!q%DNGPgqrkC_cu zBN&=OJiB(D?{zxI`GE!J?m6PxZk6vX-r&6EXY}RB^bC2Q-6tsQdUb zWq8nS^;!-+o*Gtc1r^8 zp$n9HW4jwCwkv{^(OrB0R_$GSvK!BxXxpiDt}HjU;oR{L<1D-}?=*3@Z?|iZ5qHeI zwj74LM}7C-cTL;8BfFO2g-^Uwg{PT*0^u`T| zGj29_nZ&(~Bf={kN{uTwf%y@5t4MFn_upS>S*Rp{{5f#$Y*e+95K|Cl)9i+$)=STnY{dTBzfgi zlwhmoCvu5D#y8M=t{x$mOWxJC7ACH6069!DNia^NwqwZaJh{s9FxoC{!$mPuRCp%b*k5x6d}f8u@th znaA~hqq1!l+&zKx&xx8d{VcwH>g%6$X0pM^sck1X49X4KN@n=+dYT7U!O(2 zw4#t1^wY7^jsHKxN~x{ThK|5nHzbJ)f|?d^GAQ|!oJ~QiJmr->#d&NgE-t;U zwqCJc%vO0vcQ`|>^ZfeniERs`Z2gC+<}NHZJL=a&hlji2BRHC^WtUu3-xES@%gkKu z{;bXYF8K5-Xp4R)I2nGD>yo*!r+J&^G1{+k)$}pRi1Ztjv)-B-g>Gc1LLQnJ68x+EG(gCuH}bWQe?1WE5F#WV<;eC;A2 zeoIbeQ2{Sh9HRx9I4s3f3S!U<`|>f5V#Ft-hBo#$r)BQSqp z)m1CbK(T@12oO{ZLq7u(Ik0ftZbl77GTMwQ4uO&mqou_limZA;84(1JfL5y+l#HO& zs-Trh1|=hCr6OppmO;q~TB}uoN)W(Bk!8!2C{uAmLA|b_l&Pg0l2NLHJ$v?K1uI|a`-s<^s>(kdVv>u&7>vd6*j3gr&C6*N>%7_nWoplIea$*#z66;R({Cy9M zOeVCA`v8pa4Z*%)#wao zGr~hq9EO4m1^kZSW<0Exlthx--gvJcicp0nFL9wsW%<`2nGu}@UFy2*5wJ0D*XN{m zvFr_m%9Js9xd0O%-i;Cyi?SwTGFJ9N@AC-~0FYV`gZ z*mqO`8lqZxsF~Lh-b0}T8#S2^!bb0VjprKA(Uctb2Izf{Cdp_rYPX_0DwT^7x+8^% znkwymgFhfBN7aNhE3=+O!K>9q@9$8uXm}`!_LfP8ss020s=`o4*EH5f@5i)gJcqDD zkpN{;h^aA7dMi|wFNf%$l-sL#TrL-BJlt%QtD0Y-C>rJ$2qZLzd2zdx%wRu8-J3S< zEKhluj22x`mj`LgH_6s?sp?YEg^8U>Q(Dc1iWoAK;g4CVDABBG{&|QRjd|6pF`62Z z1x;1=cBufY7!dP>chcd=>PI!*t5Q^{m7%`gn6FhUQHAx(?V-4FyHZM-^zi#g>Glv> zAj6dUFzpU;>Ah}{CJZPik2a~p;=pTy6~7^l;Y2XW+l@#jp`Rg3ATq=!!OY?2;lNFE zm@A#z3?z0_;=EVe>*>bT)>^8F?K`dF!GltX#(6WE16*ytg@^ zCLSV+a*IgHH5)1bR;V)JH!;{argtf2K_2AxOhwv9-!teRgw4bf@5j?#3VPoJ!)u%8E7GyK@pO7>YI=gp;Z(t$mF0GNrbAe8vR=J& zy+i3USU@*@b}~KHzIAPR{n~-_UTOlbCt6V*V8#(WxPT)j4(Z`gT~#IGya^GXfN@MT;)@vPI1PP-H|wO70XU` z)~`7CGlBcq&|L@#9W$xFIj2!HC?_11OX0@yq;uy9pDSZeh- z4h%Rj;J}CEK)@%5IL8Mo^tL1LPYP3afUDlq{QqJaxG^oRvY)ZyA#xYkE5FF~D0}4R zID>JTGb$HJPq|~{_48)&U;ayv2t_`jf%U{@`U_UdVxLqc{Xb7~6M$7QgJT4p6w8<= zaHIg5etyncEm*8W!PXUv5cp7|A4i{1koYq=kM?5fjjv5#E%wkRB^3K2=4Z7n1k!BsDqM1cl245%Y5-YMb&u z1s}HRUZCzewYTV>2Ja#MAK_p0Jk4jk1jYd$5?0uFrH>+o)N*g}+UpxqpwNRkrI5TB-r68PL({CLaAxO&(l@m^-f8fhpyPfdwtZ099&kLXfmLH%LLYP(=gvAS z{g(c1ue(zVck19H)+P^9uVN`HXpZ>0U3+)7W2l3l0S5*g7;s>~fdK~w92jt5z<~h= z1|0aPIIuV1GZCHp%SS${4hQ`VI56PAfCB>#3^*|0z<>h-4h%Rj;J|`^nA0H diff --git a/Tests/packages.config b/Tests/packages.config new file mode 100644 index 0000000..50f4bdd --- /dev/null +++ b/Tests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file