diff --git a/MathMLToCSharp.sln b/MathMLToCSharp.sln index 7a037fe..70b71b9 100644 --- a/MathMLToCSharp.sln +++ b/MathMLToCSharp.sln @@ -1,18 +1,21 @@  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 + .gitignore = .gitignore + LICENSE = LICENSE + Article\MmlSharp.htm = Article\MmlSharp.htm + README.md = README.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathMLToCSharp", ".\MathMLToCSharp\MathMLToCSharp.csproj", "{9C1737F0-6DCC-4CC5-8C8E-F9B4073683F8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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 +23,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 +35,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/App.xaml.cs b/MathMLToCSharp/App.xaml.cs index 91c78fa..6c05369 100644 --- a/MathMLToCSharp/App.xaml.cs +++ b/MathMLToCSharp/App.xaml.cs @@ -2,6 +2,7 @@ using System.Windows; using System.Windows.Threading; using MathMLToCSharp.Properties; +using MathMLToCSharpLib; namespace MathMLToCSharp { diff --git a/MathMLToCSharp/BuildContext.cs b/MathMLToCSharp/BuildContext.cs deleted file mode 100644 index dbd1560..0000000 --- a/MathMLToCSharp/BuildContext.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using MathMLToCSharp.Entities; -using Wintellect.PowerCollections; - -namespace MathMLToCSharp -{ - /// - /// 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. - internal 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/MathMLToCSharp/BuildContextOptions.cs b/MathMLToCSharp/BuildContextOptions.cs deleted file mode 100644 index 79688ec..0000000 --- a/MathMLToCSharp/BuildContextOptions.cs +++ /dev/null @@ -1,210 +0,0 @@ -using System; -using System.ComponentModel; - -namespace MathMLToCSharp -{ - /// - /// Options used for building the code. - /// - [Serializable] - internal 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; - - internal 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. - /// - internal 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/MathMLToCSharp/Entities/Annotation.cs b/MathMLToCSharp/Entities/Annotation.cs deleted file mode 100644 index 07a832e..0000000 --- a/MathMLToCSharp/Entities/Annotation.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.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/MathMLToCSharp/Entities/BuildablePlainSum.cs b/MathMLToCSharp/Entities/BuildablePlainSum.cs deleted file mode 100644 index e575e97..0000000 --- a/MathMLToCSharp/Entities/BuildablePlainSum.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.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(); - } - - #endregion - } -} diff --git a/MathMLToCSharp/Entities/IBuildable.cs b/MathMLToCSharp/Entities/IBuildable.cs deleted file mode 100644 index aed6355..0000000 --- a/MathMLToCSharp/Entities/IBuildable.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// This interface must be implemented by all types of expression - /// objects in the tree. - /// - public interface IBuildable - { - /// - /// 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/MathMLToCSharp/Entities/ISum.cs b/MathMLToCSharp/Entities/ISum.cs deleted file mode 100644 index 4420747..0000000 --- a/MathMLToCSharp/Entities/ISum.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MathMLToCSharp.Entities -{ - public interface ISum : IBuildable - { - string Expression(BuildContext context); - } -} diff --git a/MathMLToCSharp/Entities/Math.cs b/MathMLToCSharp/Entities/Math.cs deleted file mode 100644 index 1a4053a..0000000 --- a/MathMLToCSharp/Entities/Math.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Root element in MathML. Not to be confused with System.Math. - /// - public class Math : WithBuildableContents - { - public Math(string content) : base(new IBuildable []{}) { /* just in case */ } - public Math(IBuildable content) : base(new[]{content}) {} - public Math(IBuildable[] contents) : base(contents) {} - 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(App.BuildOptions)); - 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/MathMLToCSharp/Entities/Mfenced.cs b/MathMLToCSharp/Entities/Mfenced.cs deleted file mode 100644 index 8ce5b3c..0000000 --- a/MathMLToCSharp/Entities/Mfenced.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Code in brackets. - /// - public class Mfenced : WithBuildableContent - { - 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/MathMLToCSharp/Entities/Mfrac.cs b/MathMLToCSharp/Entities/Mfrac.cs deleted file mode 100644 index b13b66e..0000000 --- a/MathMLToCSharp/Entities/Mfrac.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.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(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/MathMLToCSharp/Entities/Mi.cs b/MathMLToCSharp/Entities/Mi.cs deleted file mode 100644 index a122a78..0000000 --- a/MathMLToCSharp/Entities/Mi.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using System.Text; -using Wintellect.PowerCollections; - -namespace MathMLToCSharp.Entities -{ - /// - /// Identifier (a.k.a. variable). - /// - public class Mi : WithTextContent - { - 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/MathMLToCSharp/Entities/Mn.cs b/MathMLToCSharp/Entities/Mn.cs deleted file mode 100644 index cc2b8d2..0000000 --- a/MathMLToCSharp/Entities/Mn.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Number. - /// - public class Mn : WithTextContent - { - 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/MathMLToCSharp/Entities/Mo.cs b/MathMLToCSharp/Entities/Mo.cs deleted file mode 100644 index 34ee872..0000000 --- a/MathMLToCSharp/Entities/Mo.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System.Collections.Specialized; -using System.Diagnostics; -using System.Text; - -namespace MathMLToCSharp.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(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/MathMLToCSharp/Entities/Mroot.cs b/MathMLToCSharp/Entities/Mroot.cs deleted file mode 100644 index 759c042..0000000 --- a/MathMLToCSharp/Entities/Mroot.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.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(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/MathMLToCSharp/Entities/Mrow.cs b/MathMLToCSharp/Entities/Mrow.cs deleted file mode 100644 index 80e823e..0000000 --- a/MathMLToCSharp/Entities/Mrow.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// A fairly generic element that does not mean anything. - /// - public class Mrow : WithBuildableContents - { - 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/MathMLToCSharp/Entities/Msqrt.cs b/MathMLToCSharp/Entities/Msqrt.cs deleted file mode 100644 index 180cbea..0000000 --- a/MathMLToCSharp/Entities/Msqrt.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Square root. - /// - public class Msqrt : WithBuildableContents - { - 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/MathMLToCSharp/Entities/Mstyle.cs b/MathMLToCSharp/Entities/Mstyle.cs deleted file mode 100644 index a1815f1..0000000 --- a/MathMLToCSharp/Entities/Mstyle.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MathMLToCSharp.Entities -{ - class Mstyle : WithBuildableContents - { - public Mstyle(IBuildable content) : base(new[]{content}) {} - public Mstyle(IBuildable[] contents) : base(contents) {} - } -} diff --git a/MathMLToCSharp/Entities/Msub.cs b/MathMLToCSharp/Entities/Msub.cs deleted file mode 100644 index eb40ac7..0000000 --- a/MathMLToCSharp/Entities/Msub.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Subscript. - /// - public class Msub : WithBinaryContent - { - 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/MathMLToCSharp/Entities/Msubsup.cs b/MathMLToCSharp/Entities/Msubsup.cs deleted file mode 100644 index 015c316..0000000 --- a/MathMLToCSharp/Entities/Msubsup.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Subscript and superscript one one variable. - /// - public class Msubsup : WithBuildableContents - { - 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/MathMLToCSharp/Entities/Msup.cs b/MathMLToCSharp/Entities/Msup.cs deleted file mode 100644 index a919467..0000000 --- a/MathMLToCSharp/Entities/Msup.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Text; -using Wintellect.PowerCollections; - -namespace MathMLToCSharp.Entities -{ - /// - /// Superscript. Taken to mean 'power of' unless the superscript parses into a *. - /// - public class Msup : WithBinaryContent - { - 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/MathMLToCSharp/Entities/Mtable.cs b/MathMLToCSharp/Entities/Mtable.cs deleted file mode 100644 index 62af3ee..0000000 --- a/MathMLToCSharp/Entities/Mtable.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text; -using MathNet.Numerics.LinearAlgebra; -using MathNet.Numerics.LinearAlgebra.Double; - -namespace MathMLToCSharp.Entities -{ - /// - /// A table. Typically used for matrices. - /// - public class Mtable : WithBuildableContents - { - static int level = 0; - public Mtable(IBuildable[] contents) : base(contents) { } - public override void Visit(StringBuilder sb, BuildContext context) - { - level++; - Matrix m = DenseMatrix.OfArray(new double[,] { { 1, 2 }, { 3, 4 } }); - var c = m.Evd().EigenVectors; - 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/MathMLToCSharp/Entities/Mtd.cs b/MathMLToCSharp/Entities/Mtd.cs deleted file mode 100644 index 0e2a64b..0000000 --- a/MathMLToCSharp/Entities/Mtd.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Table cell. - /// - public class Mtd : WithBuildableContent - { - 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/MathMLToCSharp/Entities/Mtr.cs b/MathMLToCSharp/Entities/Mtr.cs deleted file mode 100644 index 86c2fc7..0000000 --- a/MathMLToCSharp/Entities/Mtr.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - /// - /// Table row. - /// - public class Mtr : WithBuildableContents - { - 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/MathMLToCSharp/Entities/Munderover.cs b/MathMLToCSharp/Entities/Munderover.cs deleted file mode 100644 index 79e220f..0000000 --- a/MathMLToCSharp/Entities/Munderover.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - class Munderover : WithBuildableContents - { - 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/MathMLToCSharp/Entities/Null.cs b/MathMLToCSharp/Entities/Null.cs deleted file mode 100644 index 1ec9a3c..0000000 --- a/MathMLToCSharp/Entities/Null.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace MathMLToCSharp.Entities -{ - [Obsolete("Do not use", true)] - class Null - { - } -} diff --git a/MathMLToCSharp/Entities/Semantics.cs b/MathMLToCSharp/Entities/Semantics.cs deleted file mode 100644 index a960e9f..0000000 --- a/MathMLToCSharp/Entities/Semantics.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Specialized; -using Wintellect.PowerCollections; - -namespace MathMLToCSharp.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/MathMLToCSharp/Entities/WithBinaryContent.cs b/MathMLToCSharp/Entities/WithBinaryContent.cs deleted file mode 100644 index a968226..0000000 --- a/MathMLToCSharp/Entities/WithBinaryContent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Text; -using Wintellect.PowerCollections; - -namespace MathMLToCSharp.Entities -{ - public abstract class WithBinaryContent : IBuildable - { - protected readonly IBuildable first, second; - 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); - - #endregion - } -} \ No newline at end of file diff --git a/MathMLToCSharp/Entities/WithBuildableContent.cs b/MathMLToCSharp/Entities/WithBuildableContent.cs deleted file mode 100644 index 7a7e179..0000000 --- a/MathMLToCSharp/Entities/WithBuildableContent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - public abstract class WithBuildableContent : IBuildable - { - protected readonly IBuildable content; - 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); - } - - #endregion - } -} \ No newline at end of file diff --git a/MathMLToCSharp/Entities/WithBuildableContents.cs b/MathMLToCSharp/Entities/WithBuildableContents.cs deleted file mode 100644 index b3f81a5..0000000 --- a/MathMLToCSharp/Entities/WithBuildableContents.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; -using Wintellect.PowerCollections; - -namespace MathMLToCSharp.Entities -{ - public abstract class WithBuildableContents : IBuildable - { - protected 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); - } - } - - #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/MathMLToCSharp/Entities/WithTextContent.cs b/MathMLToCSharp/Entities/WithTextContent.cs deleted file mode 100644 index 69599df..0000000 --- a/MathMLToCSharp/Entities/WithTextContent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text; - -namespace MathMLToCSharp.Entities -{ - public abstract class WithTextContent : IBuildable - { - protected string content; - 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); - } - - #endregion - } -} \ No newline at end of file diff --git a/MathMLToCSharp/MainFrame.xaml.cs b/MathMLToCSharp/MainFrame.xaml.cs index 2cfb3c0..93804b6 100644 --- a/MathMLToCSharp/MainFrame.xaml.cs +++ b/MathMLToCSharp/MainFrame.xaml.cs @@ -10,95 +10,99 @@ using System.Windows.Input; using System.Windows.Media; using System.Xml.Linq; + using MathMLToCSharp.Properties; + +using MathMLToCSharpLib; + using Microsoft.CSharp; namespace MathMLToCSharp { - /// - /// Interaction logic for MainFrame.xaml - /// - public partial class MainFrame : Window - { - // Using a DependencyProperty as the backing store for ShowTree. This enables animation, styling, binding, etc... - public static readonly DependencyProperty ShowTreeProperty = - DependencyProperty.Register("ShowTree", typeof(bool), typeof(MainFrame), new UIPropertyMetadata(false)); - - public MainFrame() + /// + /// Interaction logic for MainFrame.xaml + /// + public partial class MainFrame : Window { - InitializeComponent(); - WindowState = (WindowState)Settings.Default["WindowState"]; + // Using a DependencyProperty as the backing store for ShowTree. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ShowTreeProperty = + DependencyProperty.Register("ShowTree", typeof(bool), typeof(MainFrame), new UIPropertyMetadata(false)); + + public MainFrame() + { + InitializeComponent(); + WindowState = (WindowState)Settings.Default["WindowState"]; - Version version = Assembly.GetAssembly(GetType()).GetName().Version; - Title += string.Format(" v.{0}.{1}.{2}", version.Major, version.Minor, version.Build); + Version version = Assembly.GetAssembly(GetType()).GetName().Version; + Title += string.Format(" v.{0}.{1}.{2}", version.Major, version.Minor, version.Build); #if DEBUG - Title += " DEBUG"; - ShowTree = true; + Title += " DEBUG"; + ShowTree = true; #endif - propGrid.SelectedObject = App.BuildOptions; - App.BuildOptions.PropertyChanged += delegate { tbIn_TextChanged(this, null); }; - } + propGrid.SelectedObject = App.BuildOptions; + App.BuildOptions.PropertyChanged += delegate { tbIn_TextChanged(this, null); }; + } - /// - /// When set, shows the MathML tree. - /// - public bool ShowTree - { - get { return (bool)GetValue(ShowTreeProperty); } - set { SetValue(ShowTreeProperty, value); } - } + /// + /// When set, shows the MathML tree. + /// + public bool ShowTree + { + get { return (bool)GetValue(ShowTreeProperty); } + set { SetValue(ShowTreeProperty, value); } + } - private void tbIn_TextChanged(object sender, TextChangedEventArgs e) - { - tbOut.Text = string.Empty; - // try parsing the root - XElement root; - try - { - string str = tbIn.Text; - root = XElement.Parse(str); - } - catch (Exception ex) - { - tbOut.Text = ex.Message; - return; - } - // fill the tree with xml elements - FillTree(root); - - // now set output content + private void tbIn_TextChanged(object sender, TextChangedEventArgs e) + { + tbOut.Text = string.Empty; + // try parsing the root + XElement root; + try + { + string str = tbIn.Text; + root = XElement.Parse(str); + } + catch (Exception ex) + { + tbOut.Text = ex.Message; + return; + } + // fill the tree with xml elements + FillTree(root); + + // now set output content #if RELEASE try { #endif - var m = Parser.Parse(root); - var sb = new StringBuilder(); - m.Visit(sb, new BuildContext(App.BuildOptions)); - string s = sb.ToString(); - bool useHacks = true; - - if (useHacks) - { - // here's what happens when you realize string.Replace doesn't work as it should - s = s.Replace("**", "*"); - s = s.Replace("(*", "("); - s = s.Replace(")(", ")*("); - s = s.Replace(")Math", ")*Math"); - s = s.Replace("+*", "+"); - s = s.Replace("-*", "-"); - s = s.Replace("/*", "/"); - - // hack: even nastier hack here - Regex re = new Regex(@"(\d)(\()"); - foreach (Match match in re.Matches(sb.ToString())) - { - string toReplace = match.Value; - string toReplaceWith = match.Groups[1].Value + "*" + match.Groups[2].Value; - s = s.Replace(toReplace, toReplaceWith); - } - } - - tbOut.Text = s; + var m = Parser.Parse(root); + var sb = new StringBuilder(); + m.Visit(sb, new BuildContext(App.BuildOptions)); + string s = sb.ToString(); + bool useHacks = true; + + if (useHacks) + { + // here's what happens when you realize string.Replace doesn't work as it should + s = s.Replace("**", "*"); + s = s.Replace("(*", "("); + s = s.Replace(")(", ")*("); + s = s.Replace(")Math", ")*Math"); + s = s.Replace("+*", "+"); + s = s.Replace("-*", "-"); + s = s.Replace("/*", "/"); + + // hack: even nastier hack here + Regex re = new Regex(@"(\d)(\()"); + foreach (Match match in re.Matches(sb.ToString())) + { + string toReplace = match.Value; + string toReplaceWith = match.Groups[1].Value + "*" + match.Groups[2].Value; + s = s.Replace(toReplace, toReplaceWith); + } + } + + tbOut.Text = s; #if RELEASE } catch (Exception ex) @@ -108,137 +112,137 @@ private void tbIn_TextChanged(object sender, TextChangedEventArgs e) ex.StackTrace; } #endif - } - + } - private void FillTree(XElement root) - { - if (root == null) throw new ArgumentNullException("root"); - // remove all items - mmlTree.Items.Clear(); - // add root - var rootItem = new TreeViewItem - { - Header = root.Name.LocalName, - IsExpanded = true - }; - mmlTree.Items.Add(rootItem); - - // add other items recursively - AddTreeNodes(rootItem, root.Elements()); - } - private static void AddTreeNodes(ItemsControl rootItem, IEnumerable elems) - { - if (rootItem == null) throw new ArgumentNullException("rootItem"); - if (elems == null) throw new ArgumentNullException("elems"); - foreach (var elem in elems) - { - var thisItem = new TreeViewItem + private void FillTree(XElement root) { - Header = elem.Name.LocalName + - (elem.HasElements ? "" : " [" + - (elem.Value.Length > 20 ? string.Empty : elem.Value) - + "]"), - IsExpanded = true - }; - - // if the type for this node does not exist in our wonderful little app, - // make it bold and red! - try - { - Type t = Type.GetType("MathMLToCSharp.Entities." + Parser.camel(elem.Name.LocalName)); - if (t == null) - throw new NullReferenceException(); - thisItem.Foreground = Brushes.Black; - thisItem.FontWeight = FontWeights.Normal; + if (root == null) throw new ArgumentNullException("root"); + // remove all items + mmlTree.Items.Clear(); + // add root + var rootItem = new TreeViewItem + { + Header = root.Name.LocalName, + IsExpanded = true + }; + mmlTree.Items.Add(rootItem); + + // add other items recursively + AddTreeNodes(rootItem, root.Elements()); } - catch + + private static void AddTreeNodes(ItemsControl rootItem, IEnumerable elems) { - thisItem.Foreground = Brushes.Red; - thisItem.FontWeight = FontWeights.Bold; + if (rootItem == null) throw new ArgumentNullException("rootItem"); + if (elems == null) throw new ArgumentNullException("elems"); + foreach (var elem in elems) + { + var thisItem = new TreeViewItem + { + Header = elem.Name.LocalName + + (elem.HasElements ? "" : " [" + + (elem.Value.Length > 20 ? string.Empty : elem.Value) + + "]"), + IsExpanded = true + }; + + // if the type for this node does not exist in our wonderful little app, + // make it bold and red! + try + { + Type t = Type.GetType("MathMLToCSharp.Entities." + elem.Name.LocalName); + if (t == null) + throw new NullReferenceException(); + thisItem.Foreground = Brushes.Black; + thisItem.FontWeight = FontWeights.Normal; + } + catch + { + thisItem.Foreground = Brushes.Red; + thisItem.FontWeight = FontWeights.Bold; + } + + rootItem.Items.Add(thisItem); + AddTreeNodes(thisItem, elem.Elements()); + } } - rootItem.Items.Add(thisItem); - AddTreeNodes(thisItem, elem.Elements()); - } - } - - private void lnkCopy_Click(object sender, RoutedEventArgs e) - { - Clipboard.SetText(tbOut.Text); - } - - private void lnkPaste_Click(object sender, RoutedEventArgs e) - { - tbIn.Text = Clipboard.GetText(); - } - - private void myWindow_Closing(object sender, CancelEventArgs e) - { - Settings.Default["WindowState"] = WindowState; - } + private void lnkCopy_Click(object sender, RoutedEventArgs e) + { + Clipboard.SetText(tbOut.Text); + } - private void lnkFormat_Click(object sender, RoutedEventArgs e) - { - try - { - XElement xe = XElement.Parse(tbIn.Text); - tbIn.Text = xe.ToString(); - } - catch (Exception ex) - { - tbOut.Text = ex.Message; - } - } + private void lnkPaste_Click(object sender, RoutedEventArgs e) + { + tbIn.Text = Clipboard.GetText(); + } - private void myWindow_KeyDown(object sender, KeyEventArgs e) - { - // set window to fixed size for screenshots -#if DEBUG - if (e.Key == Key.F12) - { - Width = 640; - Height = 480; - ShowTree = false; - } -#endif - } + private void myWindow_Closing(object sender, CancelEventArgs e) + { + Settings.Default["WindowState"] = WindowState; + } - /// - /// Handles the Click event of the lnkVerify control. - /// - /// The source of the event. - /// The instance containing the event data. - private void lnkVerify_Click(object sender, RoutedEventArgs e) - { // let's check to see if the code we generated compiles or not - try - { - CSharpCodeProvider p = new CSharpCodeProvider(); - CompilerParameters cp = new CompilerParameters(); - string source = "using System;" + Environment.NewLine + - "using System.Diagnostics;" + Environment.NewLine + - "namespace A { class B { static void Main() {" + Environment.NewLine + - tbOut.Text + Environment.NewLine + - "}}}"; - CompilerResults cr = p.CompileAssemblyFromSource(cp, source); - if (cr.Errors.HasErrors) + private void lnkFormat_Click(object sender, RoutedEventArgs e) { - tbOut.Text = source + Environment.NewLine; - foreach (CompilerError err in cr.Errors) - tbOut.Text += err + Environment.NewLine; + try + { + XElement xe = XElement.Parse(tbIn.Text); + tbIn.Text = xe.ToString(); + } + catch (Exception ex) + { + tbOut.Text = ex.Message; + } } - else + + private void myWindow_KeyDown(object sender, KeyEventArgs e) { - MessageBox.Show( - "Code has been verified. No errors were found.", - "Verification", MessageBoxButton.OK, MessageBoxImage.Information); + // set window to fixed size for screenshots +#if DEBUG + if (e.Key == Key.F12) + { + Width = 640; + Height = 480; + ShowTree = false; + } +#endif } - } - catch - { - } + /// + /// Handles the Click event of the lnkVerify control. + /// + /// The source of the event. + /// The instance containing the event data. + private void lnkVerify_Click(object sender, RoutedEventArgs e) + { // let's check to see if the code we generated compiles or not + try + { + CSharpCodeProvider p = new CSharpCodeProvider(); + CompilerParameters cp = new CompilerParameters(); + string source = "using System;" + Environment.NewLine + + "using System.Diagnostics;" + Environment.NewLine + + "namespace A { class B { static void Main() {" + Environment.NewLine + + tbOut.Text + Environment.NewLine + + "}}}"; + CompilerResults cr = p.CompileAssemblyFromSource(cp, source); + if (cr.Errors.HasErrors) + { + tbOut.Text = source + Environment.NewLine; + foreach (CompilerError err in cr.Errors) + tbOut.Text += err + Environment.NewLine; + } + else + { + MessageBox.Show( + "Code has been verified. No errors were found.", + "Verification", MessageBoxButton.OK, MessageBoxImage.Information); + } + } + catch + { + + } + } } - } } diff --git a/MathMLToCSharp/MathMLToCSharp.csproj b/MathMLToCSharp/MathMLToCSharp.csproj index 2552bf1..63ad981 100644 --- a/MathMLToCSharp/MathMLToCSharp.csproj +++ b/MathMLToCSharp/MathMLToCSharp.csproj @@ -43,27 +43,13 @@ - - packages\MathNet.Numerics.3.20.0\lib\net35\MathNet.Numerics.dll - - - False - .\PowerCollections.dll - 3.5 - - packages\TaskParallelLibrary.1.0.2856.0\lib\Net35\System.Threading.dll - 3.5 - - 3.5 - - @@ -88,33 +74,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - Code @@ -128,20 +89,13 @@ Settings.settings True - - - - - - ResXFileCodeGenerator Resources.Designer.cs Designer - SettingsSingleFileGenerator Settings.Designer.cs @@ -150,7 +104,12 @@ - + + + + {7e67aafd-564c-4362-b93f-7fdb912e2df7} + MathMLToCSharpLib + + + 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 d008a5a..34bec11 100644 --- a/Tests/ConversionTests.cs +++ b/Tests/ConversionTests.cs @@ -1,85 +1,82 @@ -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 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("abc")); + } + + [Test] + public void DoubleMultiSubscriptTest() + { + // LaTeX == a_{boost_{charge}} + Assert.AreEqual( + "double a_boost_charge = 0.0;" + Environment.NewLine + "a_boost_charge;", + ParseAndOutput("aboostcharge")); + } - [Test] - public void DoubleSubscriptTest() - { - Assert.AreEqual( - "double a_b_c = 0.0;" + Environment.NewLine + "a_b_c;", - ParseAndOutput(@" - - - a - - - b - c - - - - - - - -")); } - } } 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 0bbfcb6..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,25 +64,30 @@ + - - - + + - {3909e9a8-4e8a-4358-a8a9-3ba634090378} + {7e67aafd-564c-4362-b93f-7fdb912e2df7} MathMLToCSharpLib - - {9c1737f0-6dcc-4cc5-8c8e-f9b4073683f8} - MathMLToCSharp - + + + + + + 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}. + + +