Add your own alternative version
Stats
88K views 5.4K downloads 137 bookmarked
Posted
26 Oct 2011

Comments and Discussions




can cou implement a random function?





The parser supports custom functions, so you can add it yourself!





(1,1)*1 returns 1 (1.1)*1 returns 11
result is 1,1. What's the problem????





It doesn't support globalization. See a thread below for possible solution.





Andres is correct. It does not support globalization. Look for a new version to come out soon though that will support it!





((1*60)+0,1)*1
The result is 60,1
and demo returns 1
if I use (,) for decimal separators
((1*60)+0.1)*1
demo returns 61
but the correct result is 60,1, what's the problem????





Thanks for this tool!
We had just one problem with plus/minus signs. Especially with the power operator. Attached you can find some code, which sets brackets to get the right result: Attention: 10^2 = 100; (10)^2 = 100 E.g.   1*sin(Var1)^2^sin(10) => 1*(01)*sin(Var1)^((01)*2^sin(10))
Hopefully bug free  i couldn't find anymore with my test unit
Greets Herbert
public string HandleSigns(string equation)
{
if (equation.Trim().StartsWith("")  equation.Trim().StartsWith("+"))
equation = "0" + equation;
equation = Regex.Replace(equation, @"\s+", string.Empty);
string pattern =
@"[+\*/^]" +
@"(" +
@"([\+])" +
@"([azAZ09]*)" +
@"(" +
@"(?'Open'\()" +
@"[^\(\)]*" +
@")+" +
@"(" +
@"(?'CloseOpen'\))" +
@")+" +
@")" +
@"([\^]*)";
Match m = null;
while ((m = Regex.Match(equation, pattern)) != null && m.Success)
{
if (!m.Success)
break;
string newequat = equation;
if (m.Groups[0].ToString().StartsWith("^"))
{
this.SetBracketsAtPow(ref newequat, m.Groups[1].Index);
}
else
{
newequat = equation.Substring(0, m.Groups[1].Index) + "(0" + m.Groups[1].ToString()[0] + "1)*" + m.Groups[1].ToString().Substring(1) + equation.Substring(m.Groups[1].Index + m.Groups[1].Length);
}
if (newequat.Equals(equation))
break;
equation = newequat;
}
pattern =
@"[+\*/^]" +
@"(" +
@"([\+])" +
@"([azAZ09]*)" +
@")" +
@"([\^]*)";
while ((m = Regex.Match(equation, pattern)) != null && m.Success)
{
if (!m.Success)
break;
string newequat = equation;
if (m.Groups[0].ToString().StartsWith("^"))
{
this.SetBracketsAtPow(ref newequat, m.Groups[1].Index);
}
else
{
newequat = equation.Substring(0, m.Groups[1].Index) + "(0" + m.Groups[1].ToString()[0] + "1)*" + m.Groups[1].ToString().Substring(1) + equation.Substring(m.Groups[1].Index + m.Groups[1].Length);
}
if (newequat.Equals(equation))
break;
equation = newequat;
}
equation = equation.Replace("**", "^");
equation = equation.Replace("(+", "(0+");
equation = equation.Replace("(", "(0");
equation = equation.Replace(",", ",0");
equation = equation.Replace(",+", ",0+");
return equation;
}
private bool SetBracketsAtPow(ref string expression, int startIndex)
{
if (string.IsNullOrEmpty(expression)  startIndex >= expression.Length  startIndex <= 0  expression[startIndex  1] != '^')
return false;
string preExp = expression.Remove(startIndex);
string exp = expression.Substring(startIndex);
if (exp.StartsWith("+"))
{
expression = expression.Substring(1);
return true;
}
else if (exp.StartsWith(""))
{
string negativMultiplier = "((01)*";
string operators = "/*+";
exp = negativMultiplier + exp.Substring(1);
int idx = negativMultiplier.Count();
int klammernZaehler = exp[idx] == '(' ? 1 : 0;
idx++;
while (idx < exp.Length && (klammernZaehler > 0  (!operators.Contains(exp[idx])  exp[idx  1] == '^')))
{
if (exp[idx] == '(')
klammernZaehler++;
else if (exp[idx] == ')')
klammernZaehler;
idx++;
}
if (idx >= exp.Length)
expression = preExp + exp + ")";
else
expression = preExp + exp.Insert(idx  1, ")");
return true;
}
else
return true;
}
modified 19Oct12 3:51am.





Thank you for this snippet of code. I will make sure I integrate it into the next version.





Hi, I have a problem with this library. When I write for example: 10*0,5 I have 0 1,5+5 gives 10 on demo what is wrong?





Hi. The problem is that my math library does not have international support yet. You are using a comma instead of a period to signify a double number. For now, use a period (.) instead of a comma (,) and it will work fine.





Many thanks for submitting this. It has already saved me much development time in one of my projects.
My only suggestion would be to include a complete help file.
A vote of 5 (add the help file, and I'll make that a 6
Graham





Thank you for the feedback. I will make sure the help file for version 1.2 is more complete and more comprehensive. In the meantime, if you need help with anything, feel free to ask.





Great invention :P Excellent work.!





Hi, are there any new versions of this project on the way (are you planning to update this) ?





Yes, I got some big fixes coming on the way. I am thinking of creating a source repository for this, so others will be able to modify the source code.





Can't wait Any ETA ?





First off, this is a great library, thank you for putting it out there.
I'm using c++/cli to write a windows forms application using VS 2008. The parser is declared and used in Form1. I'm trying to add inverse trigonometric capability by registering custom functions:
MathParserNet::Parser parser;
parser.RegisterCustomDoubleFunction("atan",Math::Atan);
Here is the error I am getting:
error C3374: can't take address of 'System::Math::Atan' unless creating delegate instance
error C2664: 'void MathParserNet::Parser::RegisterCustomFunction(System::String ^,System::Func<T,TResult> ^)' : cannot convert parameter 2 from 'double (__clrcall *)(double)' to 'System::Func<T,TResult> ^'
Any suggestions for getting this to work? Thanks.
modified 14Mar12 20:10pm.





This also does not work:
parser.AddFunction("ATAN", gcnew FunctionArgumentList{"x"}, "x(x^3)/3+(x^5)/5");
Giving error:
error C2440: 'initializing' : cannot convert from 'const char [2]' to 'MathParserNet::FunctionArgumentList ^'





Well, I'll just keep replying to myself until I hear anything
I was able to get the second method to work:
FunctionArgumentList ^fal = gcnew FunctionArgumentList();
fal>Add("x");
parser.AddFunction("ATAN", fal, "x(x^3)/3+(x^5)/5");
So for the time being I am in business, but I sure would like to figure out how to register functions. Thanks.





I am sorry man, I am not a C++ guy at all, but I will look into it and see if I can find a work around for you!





Hi,
Could you add exp in the library?
Thank,
Cha Tre





It already has it! You can use the caret sign (^) to raise a number by a power of x. For example 3^5 will raise 3 by the power of 5.





Hello,
I suggest using compiled Regex for the next version. It gives a 30%40% performance gain when repeatedly parsing an expression using the same Parser instance.
private readonly OrderedDictionary<Tokens, Regex> _tokens;
static Regex _whitespace = new Regex("[ \\t]+", RegexOptions.Compiled);
static Regex _newline = new Regex("[\\r\\n]+", RegexOptions.Compiled);
static Regex _function = new Regex("func([azAZ_][azAZ09_]*)\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _logN = new Regex("[Ll][Oo][Gg][Nn]\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _sqrt = new Regex("[Ss][Qq][Rr][Tt]\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _sin = new Regex("[Ss][Ii][Nn]\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _cos = new Regex("[Cc][Oo][Ss]\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _tan = new Regex("[Tt][Aa][Nn]\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _abs = new Regex("[Aa][Bb][Ss]\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _log = new Regex("[Ll][Oo][Gg]\\(((?<BR>\\()(?<BR>\\))[^()]*)+\\)", RegexOptions.Compiled);
static Regex _variable = new Regex("[azAZ_][azAZ09_]*", RegexOptions.Compiled);
static Regex _float = new Regex("([09]+)?\\.+[09]+", RegexOptions.Compiled);
static Regex _integer = new Regex("[09]+", RegexOptions.Compiled);
static Regex _lparen = new Regex("\\(", RegexOptions.Compiled);
static Regex _rparen = new Regex("\\)", RegexOptions.Compiled);
static Regex _exponent = new Regex("\\^", RegexOptions.Compiled);
static Regex _modulus = new Regex("\\%", RegexOptions.Compiled);
static Regex _multiply = new Regex("\\*", RegexOptions.Compiled);
static Regex _divide = new Regex("\\/", RegexOptions.Compiled);
static Regex _add = new Regex("\\+", RegexOptions.Compiled);
static Regex _subtract = new Regex("\\", RegexOptions.Compiled);
...Constructor...
_tokens = new OrderedDictionary<Tokens, Regex>();
_tokens.Add(Tokens.Whitespace, _whitespace);
_tokens.Add(Tokens.Newline, _newline);
_tokens.Add(Tokens.Function, _function);
_tokens.Add(Tokens.LogN, _logN);
_tokens.Add(Tokens.Sqrt, _sqrt);
_tokens.Add(Tokens.Sin, _sin);
_tokens.Add(Tokens.Cos, _cos);
_tokens.Add(Tokens.Tan, _tan);
_tokens.Add(Tokens.Abs, _abs);
_tokens.Add(Tokens.Log, _log);
_tokens.Add(Tokens.Variable, _variable);
_tokens.Add(Tokens.Float, _float);
_tokens.Add(Tokens.Integer, _integer);
_tokens.Add(Tokens.Lparen, _lparen);
_tokens.Add(Tokens.Rparen, _rparen);
_tokens.Add(Tokens.Exponent, _exponent);
_tokens.Add(Tokens.Modulus, _modulus);
_tokens.Add(Tokens.Multiply, _multiply);
_tokens.Add(Tokens.Divide, _divide);
_tokens.Add(Tokens.Add, _add);
_tokens.Add(Tokens.Subtract, _subtract);
...
private void PrepareRegex()
{
_regExMatchCollection.Clear();
foreach (KeyValuePair<Tokens, Regex> pair in _tokens)
{
_regExMatchCollection.Add(pair.Key, pair.Value.Matches(_inputString));
}
}
...Peek...
foreach (KeyValuePair<Tokens, Regex> pair in _tokens)
{
Match m = pair.Value.Match(_inputString, _index);
...
}
...RegisterCustomFunction...
Regex customFuncEx = new Regex(sb.ToString());
_tokens.Insert(4, (Tokens)_customFunctionIndex, customFuncEx);
...
modified 16Dec11 9:35am.





I like the extensibility with the delegate functions.





Great job (5 stars), however the tests fail and you get exceptions when the current CultureInfo is something with a different number style than in "enUS" (for example, if "3.14" is given as "3,14", which is quite common).
The most simple workaround that works is to change the current culture to "enUS" before calling SimplifyDouble and undo the change when done parsing.
A bit more efficient and universal approach, I guess, would be to:
1. Specify CultureInfo as a SimplifyDouble or constructor parameter
2. Change Float token (_tokens.Add(Tokens.Float, "([09]+)?\\.+[09]+"); ) in Lexer.cs to be dependant on NumberFormatInfo.NumberDecimalSeparator for the given culture, maybe even simply something like this:_tokens.Add(Tokens.Float, "([09]+)?\\" + _cultureInfo.NumberFormat.NumberDecimalSeparator + "+[09]+");
3. Specify number style for float and integer .Parse() methods, like this: nc.FloatNumber = double.Parse(token.TokenValue, _cultureInfo.NumberFormat);
However, I suspect there are other parts too that need to be globalized.
Best wishes!
modified 15Nov11 18:24pm.





Thanks for bring this to my attention. I haven't thought about globalization, however, as you pointed out, it is quite common. I will look into this and incorporate it into my next release.





Your project is great! Let me suggest some optimizations you could do:
At the Simplify function, instead of:
if (_customFunctions.Any(c => c.Key.Equals(fn))) found = true; You could use:
found = _customFunctions.ContainsKey(fn);
And instead of repeating checks like :
if (funcRetval is double)
...
if (funcRetval is int)
...
if (...)
you could use "else if". If for example the return val is double , why recheck for other cases?
if (funcRetval is double)
...
else if (funcRetval is int)
...
else if (...)
Of course you could also use switch statements which makes your code a bit clearer, instead of if ... else if . Also when you define the if(token.TokenName == TokenParse.Tokens.Sqrt) etc., the cases with the most frequent use should go first. For example, checking for whitespace should be first and not after Sqrt, Sin, Log, LogN etc. which are less likely to meet!
modified 13Nov11 9:07am.





Great article. Please keep it up!





Here's a code snippet of a function plotter I'm writing:
Dim x As Integer = 0
For x = 0 To 200
parser.AddVariable("x", x)
retval = parser.SimplifyInt("100+100*sin(x/20)", MathParserNet.Parser.RoundingMethods.Round)
parser.RemoveVariable("x")
cPoints.Add(New Point(x, retval))
Next
Do I have to add and remove the "x" variable or can I just set its value somehow? The performance doesn't seem to suffer it just looks inefficient to me!
Thanks.





What you are doing will work. However, it might be more efficient to do it like this:
Dim x as Integer = 0
For x = 0 to 200
retval = parser.SimplifyInt("100+100*sin(" + x.ToString() + "/20)", MathParserNet.Parser.RoundingMethods.Round)
cPoints.Add(New Point(x, retval))
Next
Try that and see if that is more effecient.





Hi, This is the exact same way I did it when I first tried your library. The problem with using it like this is I wanted a user to be able to enter any function and then plot it. The code would then have to search and replace the values for x. Not a huge problem admittedly but I didn't want string operations in a loop.
Either way, your library is still much faster than the one I was using (I won't name it!)
Will you be adding more trigonometry functions (arc sin, inverse sin etc.) to the library?
Cheers.





Eventually. In the meantime, do not forget that you can create your own function delegate methods and create your own arc sine, etc...





really really nice work! I'm trying it in my project. Thanks a lot!





Very nice and useful article!





No need but it would be awesome if you...., yes just some formatting things.
Use var (inline code) to methods & properties
This example will return a value of 11. The thing to keep in mind about SimplifyInt() is that it will always return an Integer answer and by default, it will round the answer. SimplifyInt() has an overloaded method signature that will also allow you to specify what you want to do with the fractional part of the answer, if there is one. Here is an example: The second main method for solving equations is called SimplifyDouble() . This method, like SimplifyInt() , will solve the equation, but will always return a double answer. Here is an example of this method in action:
Apply Bold for list of items
Round  This is the default. Rounds the fraction to the nearest whole number. If the number after the decimal point is a 5 or higher, it will round the whole number up. If its a 4 or lower, it will round the whole number down.
RoundDown  This will always round the whole number down, regardless of the number after the decimal point.
RoundUp  This will always round the whole number up, regardless of the number after the decimal point.
Truncate  This will not do any rounding at all and will simply "cut off" the decimal point and any numbers after it.
A Table would be awesome
Item   Meaning of Item 

()    Parenthesis  +    Add symbol (3 + 2)      Subtract symbol (3  2)  *    Multiplication Symbol (3 * 2)  /    Divide symbol (3 / 2)  %    Modulus Symbol (3 % 2)(Divides the two numbers, but returns the remainder)  ^    Exponent Symbol (3 ^ 2)(Squares 3)  ABS    Function returns the absolute of a number (ABS(3))  SIN    Returns the sine of a number (SIN(3.14))  COS    Returns the cosine of a number (COS(3.14))  TAN    Returns the tangent of a number (TAN(3.14))  LOG    Returns the base 10 logarithm of a number.  LOGN    Returns the natural logarithm of a number.  func<name>    calls a user defined function (see Functions below) 
That's all, check entire article to change things(Because I just mentioned only couple of lines).





Sorry about that. Truth be told, I am a much better writer than a formatter. I will make some revisions though.






Really well explained and simple to use in real life. Well done.





Hi,
nice article
maybe would be useful to add some logical functions like AND, OR, NOT, XOR with variables
gtz





Really good job and useful as well! Thanks a lot!





Thank you! Just uploaded a new version today with 3 major new features. Check it out!





Awesome! I'll take a look at it tomorrow but from your statements it looks quite amazing  thanks!





Very interesting. I had a need for something like this in the past.
A couple of things. Some were already posted but I would like to ellaborate more.
Accepting Delegates and Expressions. It was pointed out that taking in an Action or delegate would be usefult, but it seems that a property expression would also be useful for variables. Something like this:
public double Area {get; private set;}
...
parser.AddVariable("Area", () => this.Area);
The parser could capture the expression and hold it then access the properties getter when it is needed. In that way you could also make the parser a singleton object and have other objects set their variables while still other ones could evaluate. This could all be done with out them needing to know about each other.
Also, why use if statements in determining the type? Seems like that should be a switch, although it still seems clunky. I saw your repsonce as to why you have one method and not 2 (SimplifyDouble, SimplifyInt). Makes sence, but at the same time why not set both? Seems like if the user cares they can do a check, or if the user already knows they need not do a check, or if the user does not care they can just take the int (or double... again they dont care).
All in all well done and very kewl.
Computers have been intelligent for a long time now. It just so happens that the program writers are about as effective as a room full of monkeys trying to crank out a copy of Hamlet.





Hi Collin, thanks for the feedback. I like your idea of the variables and property expressions. I promise to look into that more and possibly implement that in a future version. I also liked the idea of having both a Simplify() and a SimplifyInt()/SimplifyFloat(). My next version will definitely have that. This way, the programmer can choose to execute whatever method they wish.
Thanks for the feedback and any other ideas you might have are always welcome!





Hey, just wanted to let you know that I just uploaded a new version of the Math Parser. This version DOES support .NET delegate functions and it also does the int and double simplification. I still need to do the property expressions though for variables. That will come soon. But in the meantime, I hope you enjoy this new version!





Very nice, I like I think this would be very useful in a couple of my projects for runtime expression evaluation with a few enhancements, thanks for posting!
If I may make a couple suggestions, mostly just personal preference:
1) Instead of the old Cstyle error mechanism of checking the return value to determine if an error occured (good old if (result == NULL) everywhere), why not just use the more standard method of throwing exceptions in C#?
2) Just a slight icky feeling about the "style" of the return value interface, again feels very Cstyle to me. In just about any practical situation, someone will expect either an int or a double result and work with it that way, even if it can be expressed as the other type. I would prefer the following method signatures:
double SimplifyDouble();
int SimplifyInt();
object Simplify();
dynamic SimplifyDynamic();
which would let you choose how you want to work with it.
3) It would be wicked cool if there was an overload of AddFunction that took a function delegate as a parameter. You could do something like:
dynamic CalculateArea(dynamic w, dynamic h)
{
return w * h;
}
parser.AddFunction("CalculateArea", CalculateArea);
This is a bad example because it is simple, but you get the idea. The CalculateArea function could do just about anything, i.e. call a web service to retrieve a complex value, pull data from other parts of the application, etc. AddVariable could also get an overload that takes a delegate parameter so the value of the variable can be resolved at the time the equation is evaluated.





Hi Mike. Thanks for comments on my Math Parser. To tackle your suggestions:
1) Because I come from a C background, I guess that is partly why I subconsciously used the "old Cstyle error mechanism of checking the return value for an error". It would be a simple change to use C#'s throw to create an error. I can do this.
2) The reason I have just one Simplify() function, rather then a SimplifyInt() or SimplifyDouble() etc., is two reasons. One I wanted to keep it simple. I wanted one universal function instead of a bunch of overloaded functions. Perhaps by keeping it simple, I am overshadowing the usefulness of the program. The second reason is because I didn't want the user of the library to have to know anything about the expression they are passing into the Simplify() function. In other words, let's say you have a program in which its required that the user of your program types in an equation. How would you know to call SimplifyDouble() or SimplifyInt(), without doing some parsing of your own? Does that kind of make sense? Maybe in the future though, I will take your advice and overload the Simplify function, as you suggest.
3) I actually had this same idea the other night and I am actually in the process of implementing exactly what you suggested: programmically creating methods that can be passed as function delegates in an effort to extend the Math Parser. Look for this feature in a future version!
Thanks for the comments Mike. Please review my responses to your comments and if, after reading my justifications, let me know if you think its still better to implement 1 and 2 and I will consider it for the next version!





I do think exceptions would be much more inline with standard .NET behavior, so I'm all for that change.
If the user doesn't care about whether the expression returns an int or double, then can use the function that returns object and typecheck, or the version that returns dynamic if they want to work with the value further without explicitly casting.
I just think that usually, the value will have to go into a database or control or a variable somewhere which will be constrained by type. If the application will allow decimal values, the underlying storage will have to be double anyway, so instead of checking the return type all the time they can just use SimplifyDouble() and always get the result as a double. If they require an int return, using SimplifyInt() and throwing an error if return is not an int makes more sense to me, possibly with an overload that allows you to specify exception/round/roundup/rounddown behavior explicitly.
The library user has to know expression behavior anyway because they are forced to check return type after. They can do that with dynamic or object return functions if they want to do that.
Just my personal opinion, I'm being picky lol. Either way you have my 5, awesome stuff





Alright Mike, you talked me into it. First, I will definitely get rid of the Error enumeration and ErrorValue property and I will, instead, throw exceptions whenever a problem occurs.
Also, I will keep my Simplify() function the way it is, but I will create SimplifyDouble() and SimplifyInt() functions. Really this is a simple change. The functions only need to call Simplify(), then return the SimplificationReturnValue.FloatValue or SimplificationReturnValue.IntValue.
I like the idea of returning a dynamic type. My only hesitation though is I want the code to be compatible with the .NET 3.5 framework. I will look more into this though







General News Suggestion Question Bug Answer Joke Praise Rant Admin Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

