mathexpr: Introduce changes by Jean-Philippe Meuret

This commit is contained in:
Xavier Del Campo Romero 2024-04-14 22:02:55 +02:00
parent 520274a73d
commit 0dab63bf3b
Signed by: xavi
GPG Key ID: 84FF3612A9BF43F2
2 changed files with 200 additions and 35 deletions

View File

@ -1,6 +1,6 @@
/* /*
mathexpr.cpp version 2.0 mathexpr.cpp version 2.1
Copyright (c) 1997-2000 Yann OLLIVIER Copyright (c) 1997-2000 Yann OLLIVIER
@ -12,7 +12,33 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
*/ */
#include"mathexpr.h" /* 2009 changes by Jean-Philippe Meuret <jpmeuret@free.fr> for Qatsh
- added EFunction class to support external functions with any number of parameters
- fixed memory leaks in ROperation::Expr() in the Num, Var and Error cases
- added code comments in several places
- ROperation::ROperation(const char*,int,PRVar*,int,PRFunction*) :
changed 1st arg from 'char*' to 'const char*' for GCC 4.2 (deprecated conversion)
- optimised IsNumeric(char), IsFunction(const char*,int,int,PRFunction*),
and IsolateVars(...).
- added parenthesis at line 513 on GCC suggestion :
before: if(s[i]==opc&&(op!=Sub||i&&s[i-1]==')'))return i;
after : if(s[i]==opc&&(op!=Sub||(i&&s[i-1]==')')))return i;
TODO:
- make code more readable (1 instruction per line, consistent indentation,
at least all the end-of-block '}' on their own line, ...)
- give member data a special name, for easier understanding (ex: a leading "_" or "m_")
- reimplement code parts without any goto instruction
- add more comments
- use constants everywhere the same litteral is used more than once in the code
- use strxxx standard libC functions for all string manipulations
- and even better ... move all "[const] char*" to sdt::string
*/
#include <stdarg.h>
#include "mathexpr.h"
char* MidStr(const char*s,int i1,int i2) char* MidStr(const char*s,int i1,int i2)
{ {
@ -27,10 +53,12 @@ char* MidStr(const char*s,int i1,int i2)
s1[i2-i1+1]=0;return s1; s1[i2-i1+1]=0;return s1;
} }
// Return a copy of the given string (use strdup ?)
char* CopyStr(const char*s) char* CopyStr(const char*s)
{char*s1=new char[strlen(s)+1];char*s12=s1;const char*s2=s; {char*s1=new char[strlen(s)+1];char*s12=s1;const char*s2=s;
while((*s12++=*s2++));return s1;} while((*s12++=*s2++));return s1;}
// Insert a char at a given position in a string (increase string length by 1)
void InsStr(char*&s,int n,char c)// Warning : deletes the old string void InsStr(char*&s,int n,char c)// Warning : deletes the old string
{if(n<0||n>(int)strlen(s))return; {if(n<0||n>(int)strlen(s))return;
char*s1=new char[strlen(s)+2]; char*s1=new char[strlen(s)+2];
@ -48,6 +76,7 @@ for(i=0;s[i];i++)if(s[i]!=s2[i])return 0;
return 1; return 1;
} }
// Tell if s[n...] == s2
signed char CompStr(const char*s,int n,const char*s2) signed char CompStr(const char*s,int n,const char*s2)
{if(n<0||n>=(int)strlen(s)||n+(int)strlen(s2)>(int)strlen(s))return 0; {if(n<0||n>=(int)strlen(s)||n+(int)strlen(s2)>(int)strlen(s))return 0;
int i; int i;
@ -72,7 +101,49 @@ RVar::RVar(const char*namep,double*pvalp)
{pval=pvalp;name=CopyStr(namep);} {pval=pvalp;name=CopyStr(namep);}
RVar::~RVar() RVar::~RVar()
{if(name!=NULL)delete[] name;} {
if(name!=NULL)delete[] name;
}
EFunction::EFunction(unsigned nvarsp)
{
nvars = nvarsp;
pvars = new double[nvarsp];
}
EFunction::~EFunction()
{
if (pvars)
delete[] pvars;
}
double EFunction::operator()(double* pvarsp)
{
// Store variables in the pvars array.
unsigned i;
for (i = 0; i < nvars; i++)
pvars[i] = pvarsp[i];
// Compute value.
return Val();
}
double EFunction::operator()(double var1p, ...)
{
// Store 1st variable (always here) in the pvars array.
pvars[0] = var1p;
// Store other variables in the pvars array.
va_list vl;
va_start(vl, var1p);
unsigned i;
for (i = 1; i < nvars; i++)
pvars[i] = (double)va_arg(vl, double);
va_end(vl);
// Compute value.
return Val();
}
RFunction::RFunction() RFunction::RFunction()
{ {
@ -86,11 +157,18 @@ RFunction::RFunction(double ((*pfuncvalp)(double)))
nvars=1;ppvar=NULL;op=ErrVal;buf=NULL; nvars=1;ppvar=NULL;op=ErrVal;buf=NULL;
} }
RFunction::RFunction(EFunction& efunc)
{
type=2;pfuncobjval=&efunc;name=new char[1];name[0]=0;
nvars=efunc.nvars;ppvar=NULL;op=ErrVal;buf=NULL;
}
RFunction::RFunction(const RFunction& rfunc) RFunction::RFunction(const RFunction& rfunc)
{ {
if(this==&rfunc)return; if(this==&rfunc)return;
type=rfunc.type;op=rfunc.op; type=rfunc.type;op=rfunc.op;
pfuncval=rfunc.pfuncval; pfuncval=rfunc.pfuncval;
pfuncobjval=rfunc.pfuncobjval;
name=CopyStr(rfunc.name); name=CopyStr(rfunc.name);
nvars=rfunc.nvars; nvars=rfunc.nvars;
if(rfunc.ppvar!=NULL&&nvars){ if(rfunc.ppvar!=NULL&&nvars){
@ -129,6 +207,7 @@ RFunction& RFunction::operator=(const RFunction& rfunc)
if(this==&rfunc)return *this; if(this==&rfunc)return *this;
type=rfunc.type;op=rfunc.op; type=rfunc.type;op=rfunc.op;
pfuncval=rfunc.pfuncval; pfuncval=rfunc.pfuncval;
pfuncobjval=rfunc.pfuncobjval;
delete[]name; delete[]name;
name=CopyStr(rfunc.name); name=CopyStr(rfunc.name);
if(ppvar!=NULL)delete[]ppvar; if(ppvar!=NULL)delete[]ppvar;
@ -150,6 +229,7 @@ double RFunction::Val(double x) const
{ {
if(type==-1||nvars>=2)return ErrVal; if(type==-1||nvars>=2)return ErrVal;
if(type==0)return (*pfuncval)(x); if(type==0)return (*pfuncval)(x);
if(type==2)return (*pfuncobjval)(x);
double xb=*(*ppvar)->pval,y; double xb=*(*ppvar)->pval,y;
*(*ppvar)->pval=x; // Warning : could cause trouble if this value is used in a parallel process *(*ppvar)->pval=x; // Warning : could cause trouble if this value is used in a parallel process
y=op.Val(); y=op.Val();
@ -161,6 +241,7 @@ double RFunction::Val(double*pv) const
{ {
if(type==-1)return ErrVal; if(type==-1)return ErrVal;
if(type==0)return (*pfuncval)(*pv); if(type==0)return (*pfuncval)(*pv);
if(type==2)return (*pfuncobjval)(pv);
double y; double y;
int i; int i;
for(i=0;i<nvars;i++){ for(i=0;i<nvars;i++){
@ -344,8 +425,8 @@ ROperation ApplyOperator(int n,ROperation**pops,ROperation (*func)(const ROperat
ROperation RFunction::operator()(const ROperation& op) ROperation RFunction::operator()(const ROperation& op)
{ {
/* Code to use to replace explcitly instead of using a pointer to /* Code to use to replace explicitly instead of using a pointer to
if(nvars!=op.NMembers()||type==-1||type==0)return ErrVal; if(nvars!=op.NMembers()||type==-1||type==0||type==2)return ErrVal;
ROperation op2=*pop;int i; ROperation op2=*pop;int i;
RVar**ppvar2=new PRVar[nvars];char s[11]=""; RVar**ppvar2=new PRVar[nvars];char s[11]="";
for(i=0;i<nvars;i++){ for(i=0;i<nvars;i++){
@ -364,24 +445,29 @@ ROperation RFunction::operator()(const ROperation& op)
return op2; return op2;
} }
//Auxiliary string functions //Auxiliary string functions=====================================================
void SupprSpaces(char*&s)//Deletes the old string // Remove spaces, tabs and line feeds (deletes the old string)
void SupprSpaces(char*&s)
{ {
int i; int i;
for(i=0;s[i];i++)if(s[i]==' '||s[i]=='\t'||s[i]=='\n')DelStr(s,i--); for(i=0;s[i];i++)if(s[i]==' '||s[i]=='\t'||s[i]=='\n')DelStr(s,i--);
} }
// Tell if a char is part of a decimal literal (decimal digits and decimal dot).
signed char IsNumeric(char c) signed char IsNumeric(char c)
{if(c!='0'&&c!='1'&&c!='2'&&c!='3'&&c!='4' {
&&c!='5'&&c!='6'&&c!='7'&&c!='8'&&c!='9'&&c!='.')return 0; if((c<'0'||c>'9')&&c!='.')
return 1; return 0;
return 1;
} }
// Tell if a string is a decimal literal (TODO: fix positive answer even when multiple dots).
signed char IsTNumeric(char *s) signed char IsTNumeric(char *s)
{int i;for(i=0;i<(int)strlen(s);i++)if(!IsNumeric(s[i]))return 0;return 1; {int i;for(i=0;i<(int)strlen(s);i++)if(!IsNumeric(s[i]))return 0;return 1;
} }
//Searchs the corresponding bracket of an opening bracket
int SearchCorOpenbracket(char*s,int n) //Searchs the corresponding bracket of an opening bracket int SearchCorOpenbracket(char*s,int n) //Searchs the corresponding bracket of an opening bracket
{if(n>=(int)strlen(s)-1)return -1; {if(n>=(int)strlen(s)-1)return -1;
int i,c=1; int i,c=1;
@ -392,6 +478,7 @@ for(i=n+1;s[i];i++){
return -1; return -1;
} }
//Searchs the corresponding bracket of a closing bracket
int SearchCorClosebracket(char*s,int n) //Searchs the corresponding bracket of a closing bracket int SearchCorClosebracket(char*s,int n) //Searchs the corresponding bracket of a closing bracket
{if(n<1)return -1; {if(n<1)return -1;
int i,c=1; int i,c=1;
@ -419,17 +506,21 @@ int SearchOperator(char*s,ROperator op)
}; };
int i; int i;
for(i=(int)strlen(s)-1;i>=0;i--){ for(i=(int)strlen(s)-1;i>=0;i--){
if(s[i]==opc&&(op!=Sub||i&&s[i-1]==')'))return i; if(s[i]==opc&&(op!=Sub||(i&&s[i-1]==')')))return i;
if(s[i]==')'){i=SearchCorClosebracket(s,i);if(i==-1)return -1;}; if(s[i]==')'){i=SearchCorClosebracket(s,i);if(i==-1)return -1;};
}; };
return -1; return -1;
} }
void SimplifyStr(char*&s) //Warning : deletes the old string void SimplifyStr(char*&s) //Warning : deletes the old string
{if(!strlen(s))return; {
// Nothing more to do if string is empty
if(!strlen(s))return;
// Remove useless matching parenthesis (1st and last char)
char*s1=s,*s2=s+strlen(s);signed char ind=0; char*s1=s,*s2=s+strlen(s);signed char ind=0;
if(s1[0]=='('&&SearchCorOpenbracket(s1,0)==s2-s1-1){ if(s1[0]=='('&&SearchCorOpenbracket(s1,0)==s2-s1-1){
s1++;s2--;ind=1;} s1++;s2--;ind=1;}
// Return an empty string if nothing remains
if(s1==s2) if(s1==s2)
{ {
delete[]s; delete[]s;
@ -437,7 +528,9 @@ if(s1==s2)
s[0]=0; s[0]=0;
return; return;
} }
// Remove any leading spaces
if(s1[0]==' '){ind=1;while(s1[0]==' '&&s1<s2)s1++;} if(s1[0]==' '){ind=1;while(s1[0]==' '&&s1<s2)s1++;}
// Return an empty string if nothing remains
if(s1==s2) if(s1==s2)
{ {
delete[]s; delete[]s;
@ -445,14 +538,19 @@ if(s1==s2)
s[0]=0; s[0]=0;
return; return;
} }
// Remove any ending spaces
if(*(s2-1)==' '){ind=1;while(s2>s1&&*(s2-1)==' ')s2--;} if(*(s2-1)==' '){ind=1;while(s2>s1&&*(s2-1)==' ')s2--;}
*s2=0; *s2=0;
// Replace input string by simplified one
s1=CopyStr(s1);delete[]s;s=s1; s1=CopyStr(s1);delete[]s;s=s1;
// Continue simplification (recursive) if needed
if(ind)SimplifyStr(s); if(ind)SimplifyStr(s);
} }
int max(int a, int b){return (a>b?a:b);} int max(int a, int b){return (a>b?a:b);}
// Detect the name of a variable from the given list, and return its length ;
// the longest possible name is kept (xyz wins against xy)
int IsVar(const char*s,int n,int nvar,PRVar*ppvar) int IsVar(const char*s,int n,int nvar,PRVar*ppvar)
{ {
if(n<0||n>(int)strlen(s))return 0; if(n<0||n>(int)strlen(s))return 0;
@ -461,6 +559,7 @@ int IsVar(const char*s,int n,int nvar,PRVar*ppvar)
return l; return l;
} }
// Detect the name of a standard function, and return its length
int IsFunction(const char*s,int n) int IsFunction(const char*s,int n)
{ {
if(CompStr(s,n,"sin")||CompStr(s,n,"cos")||CompStr(s,n,"exp") if(CompStr(s,n,"sin")||CompStr(s,n,"cos")||CompStr(s,n,"exp")
@ -474,15 +573,17 @@ int IsFunction(const char*s,int n)
return 0; return 0;
} }
// Detect the name of a function (be it a user defined one from the given list, or a standard one),
// and return its length ; the longest possible name is kept (xyz wins against xy)
// Limitations: standard functions are searched first, and may mask user defined ones
// whose name begins with a standard one (ex: user "sine" masked by standard "sin")
// WARNING: IF PATCHED TO DO OTHERWISE, SHOULD BE PATCHED TOGETHER WITH THE
// PARSER BELOW which treats standard functions before user-defined ones
int IsFunction(const char*s,int n,int nfunc,PRFunction*ppfunc) int IsFunction(const char*s,int n,int nfunc,PRFunction*ppfunc)
//Not recognized if a user-defined function is eg "sine" ie begins like
//a standard function
//IF PATCHED TO DO OTHERWISE, SHOULD BE PATCHED TOGETHER WITH THE
//PARSER BELOW which treats standard functions before user-defined ones
{ {
int l=IsFunction(s,n); int l=IsFunction(s,n);
if(l)return l; if(l)return l;
int i;l=0; int i;
for(i=0;i<nfunc;i++)if(CompStr(s,n,ppfunc[i]->name))l=max(l,strlen(ppfunc[i]->name)); for(i=0;i<nfunc;i++)if(CompStr(s,n,ppfunc[i]->name))l=max(l,strlen(ppfunc[i]->name));
return l; return l;
} }
@ -492,49 +593,68 @@ signed char IsFunction(ROperator op)
op==Atan||op==Asin||op==Acos||op==Atan||op==Sqrt||op==Opp); op==Atan||op==Asin||op==Acos||op==Atan||op==Sqrt||op==Opp);
} }
// Enclose numbers between parenthesis (deletes the old string).
void IsolateVars(char*&s,int nvar,PRVar*ppvar,int nfunc,PRFunction*ppfunc)//Deletes the old string void IsolateVars(char*&s,int nvar,PRVar*ppvar,int nfunc,PRFunction*ppfunc)//Deletes the old string
{ {
int i,j; int i,j;
i=0; i=0;
for(i=0;s[i];i++){ for(i=0;s[i];i++){
// Jump over a (...) bloc if any
if(s[i]=='('){i=SearchCorOpenbracket(s,i);if(i==-1)return;continue;}; if(s[i]=='('){i=SearchCorOpenbracket(s,i);if(i==-1)return;continue;};
// When detecting a variable or constant name, if no longer function name can match,
// and if enclose it between ( and )
if(((j=IsVar(s,i,nvar,ppvar))>IsFunction(s,i,nfunc,ppfunc))||((CompStr(s,i,"pi")||CompStr(s,i,"PI")||CompStr(s,i,"Pi"))&&(j=2))){ if(((j=IsVar(s,i,nvar,ppvar))>IsFunction(s,i,nfunc,ppfunc))||((CompStr(s,i,"pi")||CompStr(s,i,"PI")||CompStr(s,i,"Pi"))&&(j=2))){
InsStr(s,i,'(');InsStr(s,i+j+1,')');i+=j+1;continue;}; InsStr(s,i,'(');InsStr(s,i+j+1,')');i+=j+1;continue;};
if(IsFunction(s,i,nfunc,ppfunc)){i+=IsFunction(s,i,nfunc,ppfunc)-1;if(!s[i])return;continue;}; if((j=IsFunction(s,i,nfunc,ppfunc))){i+=j-1;if(!s[i])return;continue;};
}; };
} }
// Enclose numbers between parenthesis (deletes the old string).
void IsolateNumbers(char*&s,int nvar,RVar**ppvar,int nfunc,RFunction**ppfunc)//Deletes the old string void IsolateNumbers(char*&s,int nvar,RVar**ppvar,int nfunc,RFunction**ppfunc)//Deletes the old string
{ {
int i,i2,ind=0,t1,t2; int i,i2=0,ind=0,t1,t2;
for(i=0;s[i];i++){ for(i=0;s[i];i++){
// When detecting the end of a number, enclose it between ( and )
if(ind&&!IsNumeric(s[i])){ind=0;InsStr(s,i2,'(');i++;InsStr(s,i,')');continue;}; if(ind&&!IsNumeric(s[i])){ind=0;InsStr(s,i2,'(');i++;InsStr(s,i,')');continue;};
// Jump over a variable or a function name if any
t1=IsVar(s,i,nvar,ppvar);t2=IsFunction(s,i,nfunc,ppfunc); t1=IsVar(s,i,nvar,ppvar);t2=IsFunction(s,i,nfunc,ppfunc);
if(t1||t2){i+=max(t1,t2)-1;continue;}; if(t1||t2){i+=max(t1,t2)-1;continue;};
// Jump over a (...) bloc if any (the remainder of the function "call")
if(s[i]=='('){i=SearchCorOpenbracket(s,i);if(i==-1)return;continue;}; if(s[i]=='('){i=SearchCorOpenbracket(s,i);if(i==-1)return;continue;};
// Here, we detect the beginning of a number
if(!ind&&IsNumeric(s[i])){i2=i;ind=1;}; if(!ind&&IsNumeric(s[i])){i2=i;ind=1;};
}; };
// If the end of a number has not yet been detected, now it _is_, so enclose it between ( and )
if(ind)InsStr(s,i2,'(');i++;InsStr(s,i,')'); if(ind)InsStr(s,i2,'(');i++;InsStr(s,i,')');
} }
ROperation::ROperation(char*sp,int nvar,PRVar*ppvarp,int nfuncp,PRFunction*ppfuncp) ROperation::ROperation(const char*sp,int nvar,PRVar*ppvarp,int nfuncp,PRFunction*ppfuncp)
{ {
ValC=ErrVal;mmb1=NULL;mmb2=NULL;pvar=NULL;op=ErrOp;pvarval=NULL;containfuncflag=0;pfunc=NULL;pinstr=NULL;pvals=NULL;ppile=NULL;pfuncpile=NULL; ValC=ErrVal;mmb1=NULL;mmb2=NULL;pvar=NULL;op=ErrOp;pvarval=NULL;containfuncflag=0;pfunc=NULL;pinstr=NULL;pvals=NULL;ppile=NULL;pfuncpile=NULL;
int i,j,k,l;signed char flag=1; int i,j,k,l;signed char flag=1;
char*s=CopyStr(sp),*s1=NULL,*s2=NULL; char*s=CopyStr(sp),*s1=NULL,*s2=NULL;
// Simplify formula and remove leading : and ; (why ?)
SimplifyStr(s);if(!s[0]||!strcmp(s,"Error")){goto fin;} SimplifyStr(s);if(!s[0]||!strcmp(s,"Error")){goto fin;}
while(s[0]==':'||s[0]==';'){ while(s[0]==':'||s[0]==';'){
s1=CopyStr(s+1);delete[]s;s=s1;s1=NULL; s1=CopyStr(s+1);delete[]s;s=s1;s1=NULL;
SimplifyStr(s);if(!s[0]||!strcmp(s,"Error")){goto fin;} SimplifyStr(s);if(!s[0]||!strcmp(s,"Error")){goto fin;}
} }
// Case 1: Formula = Integer or decimal litteral, or predefined constant litteral (pi)
if(IsTNumeric(s)){op=Num;ValC=atof(s);mmb1=NULL;mmb2=NULL;goto fin;}; if(IsTNumeric(s)){op=Num;ValC=atof(s);mmb1=NULL;mmb2=NULL;goto fin;};
if(EqStr(s,"pi")||EqStr(s,"PI")||EqStr(s,"Pi")) if(EqStr(s,"pi")||EqStr(s,"PI")||EqStr(s,"Pi"))
{op=Num;ValC=3.141592653589793238462643383279L;mmb1=NULL;mmb2=NULL;goto fin;}; {op=Num;ValC=3.141592653589793238462643383279L;mmb1=NULL;mmb2=NULL;goto fin;};
// Case 2: Formula = Variable with a name that is longer than a matching user defined or standard
// function (Note: remaining chars are ignored if any !).
if(IsFunction(s,0,nfuncp,ppfuncp)<IsVar(s,0,nvar,ppvarp)) if(IsFunction(s,0,nfuncp,ppfuncp)<IsVar(s,0,nvar,ppvarp))
for(i=0;i<nvar;i++)if(EqStr(s,(*(ppvarp+i))->name)) for(i=0;i<nvar;i++)if(EqStr(s,(*(ppvarp+i))->name))
{pvar=ppvarp[i];pvarval=pvar->pval;op=Var;mmb1=NULL;mmb2=NULL;goto fin;}; {pvar=ppvarp[i];pvarval=pvar->pval;op=Var;mmb1=NULL;mmb2=NULL;goto fin;};
// Case 3: Something else.
for(k=0;s[k];k++){ for(k=0;s[k];k++){
// Jump over a (...) block.
if(s[k]=='('){k=SearchCorOpenbracket(s,k);if(k==-1)break;continue;}; if(s[k]=='('){k=SearchCorOpenbracket(s,k);if(k==-1)break;continue;};
// Detect a function (user defined or standard one), with a name that is longer
// than the one of any matching variable => add a : or ; at the end of its name
// (; if f(...) notation used, : if ????)
if((l=IsFunction(s,k,nfuncp,ppfuncp))&&l>=IsVar(s,k,nvar,ppvarp)){ if((l=IsFunction(s,k,nfuncp,ppfuncp))&&l>=IsVar(s,k,nvar,ppvarp)){
i=k+l;while(s[i]==' ')i++; i=k+l;while(s[i]==' ')i++;
if(s[i]=='('){ if(s[i]=='('){
@ -543,38 +663,49 @@ ROperation::ROperation(char*sp,int nvar,PRVar*ppvarp,int nfuncp,PRFunction*ppfun
}else if(s[i]!=':'&&s[i]!=';'){InsStr(s,i,':');k=i;} }else if(s[i]!=':'&&s[i]!=';'){InsStr(s,i,':');k=i;}
} }
} }
// Enclose any number, constant (Pi) and var name between parenthesis,
// and remove any (useless) space, tab or new line.
IsolateNumbers(s,nvar,ppvarp,nfuncp,ppfuncp); IsolateNumbers(s,nvar,ppvarp,nfuncp,ppfuncp);
if(nvar)IsolateVars(s,nvar,ppvarp,nfuncp,ppfuncp); if(nvar)IsolateVars(s,nvar,ppvarp,nfuncp,ppfuncp);
SupprSpaces(s); SupprSpaces(s);
// Case 3a: "," binary operator (Juxtaposition)
i=SearchOperator(s,Juxt); i=SearchOperator(s,Juxt);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
op=Juxt;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp); op=Juxt;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Case 3b: "+" binary operator (Addition)
i=SearchOperator(s,Add); i=SearchOperator(s,Add);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
op=Add;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp); op=Add;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Case 3c: "-" binary operator (Substraction)
i=SearchOperator(s,Sub); i=SearchOperator(s,Sub);
if(i!=-1){ if(i!=-1){
s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
op=Sub;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp); op=Sub;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Case 3d: "-" unary operator (Negation)
if(s[0]=='-'){s2=MidStr(s,1,strlen(s)-1); if(s[0]=='-'){s2=MidStr(s,1,strlen(s)-1);
op=Opp;mmb1=NULL; op=Opp;mmb1=NULL;
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Enclose function "calls" between parenthesis
for(i=0;s[i];i++){ for(i=0;s[i];i++){
// Jump over a (...) block.
if(s[i]=='('){i=SearchCorOpenbracket(s,i);if(i==-1)break;continue;}; if(s[i]=='('){i=SearchCorOpenbracket(s,i);if(i==-1)break;continue;};
// Detecting a function "call" :
if(IsFunction(s,i,nfuncp,ppfuncp)){ if(IsFunction(s,i,nfuncp,ppfuncp)){
k=i+IsFunction(s,i,nfuncp,ppfuncp);while(s[k]==' ')k++; k=i+IsFunction(s,i,nfuncp,ppfuncp);while(s[k]==' ')k++; // Ignore spaces (Is it usefull, as SupprSpaces was called before ?)
// f(...) notation => enclose it between parenthesis (=> "(f(...))" )
if(s[k]==';'){ if(s[k]==';'){
// s=DelStr(s,k); // s=DelStr(s,k);
j=k;while(s[j]!='(')j++; j=k;while(s[j]!='(')j++;
j=SearchCorOpenbracket(s,j); j=SearchCorOpenbracket(s,j);
if(j!=-1){InsStr(s,j,')');InsStr(s,i,'(');i=j+2;} if(j!=-1){InsStr(s,j,')');InsStr(s,i,'(');i=j+2;}
// ??? => enclose it between parenthesis (=> "(???)" )
}else if(s[k]==':'){ }else if(s[k]==':'){
// s=DelStr(s,k); // s=DelStr(s,k);
for(j=k;s[j];j++) for(j=k;s[j];j++)
@ -591,8 +722,10 @@ ROperation::ROperation(char*sp,int nvar,PRVar*ppvarp,int nfuncp,PRFunction*ppfun
} }
} }
} }
// Add explicit multiply operator "*" anywhere implicit ( (...)<here>(...) )
for(i=0;s[i]&&s[i+1];i++)if(s[i]==')'&&s[i+1]=='(') for(i=0;s[i]&&s[i+1];i++)if(s[i]==')'&&s[i+1]=='(')
InsStr(s,++i,'*'); InsStr(s,++i,'*');
// Case 3e: Standard or user defined function
if(s[0]=='('&&SearchCorOpenbracket(s,0)==(int)strlen(s)-1){ if(s[0]=='('&&SearchCorOpenbracket(s,0)==(int)strlen(s)-1){
if(CompStr(s,1,"exp")){op=Exp;s2=MidStr(s,4,strlen(s)-2);} if(CompStr(s,1,"exp")){op=Exp;s2=MidStr(s,4,strlen(s)-2);}
else if(CompStr(s,1,"abs")){op=Abs;s2=MidStr(s,4,strlen(s)-2);} else if(CompStr(s,1,"abs")){op=Abs;s2=MidStr(s,4,strlen(s)-2);}
@ -622,21 +755,25 @@ ROperation::ROperation(char*sp,int nvar,PRVar*ppvarp,int nfuncp,PRFunction*ppfun
if(op==Fun)if(mmb2->NMembers()!=pfunc->nvars){op=ErrOp;mmb1=NULL;mmb2=NULL;goto fin;} if(op==Fun)if(mmb2->NMembers()!=pfunc->nvars){op=ErrOp;mmb1=NULL;mmb2=NULL;goto fin;}
goto fin; goto fin;
}; };
// Case 3f: "*" binary operator (Multiplication)
i=SearchOperator(s,Mult); i=SearchOperator(s,Mult);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
op=Mult;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp); op=Mult;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Case 3g: "/" binary operator (Division)
i=SearchOperator(s,Div); i=SearchOperator(s,Div);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
op=Div;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp); op=Div;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Case 3h: "^" binary operator (Power)
i=SearchOperator(s,Pow); i=SearchOperator(s,Pow);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
op=Pow;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp); op=Pow;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Case 3i: "#" binary operator (Square or N>2 th root)
i=SearchOperator(s,NthRoot); i=SearchOperator(s,NthRoot);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
if(i==0||s[i-1]!=')') if(i==0||s[i-1]!=')')
@ -644,12 +781,17 @@ ROperation::ROperation(char*sp,int nvar,PRVar*ppvarp,int nfuncp,PRFunction*ppfun
{op=NthRoot;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);}; {op=NthRoot;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);};
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Case 3k: "E" binary operator (power of 10)
i=SearchOperator(s,E10); i=SearchOperator(s,E10);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1); if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-1);
op=E10;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp); op=E10;mmb1=new ROperation(s1,nvar,ppvarp,nfuncp,ppfuncp);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin; mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
}; };
// Other cases: Error.
op=ErrOp;mmb1=NULL;mmb2=NULL; op=ErrOp;mmb1=NULL;mmb2=NULL;
//
fin: fin:
BuildCode(); BuildCode();
delete[]s;if(s1!=NULL)delete[] s1;if(s2!=NULL)delete[]s2; delete[]s;if(s1!=NULL)delete[] s1;if(s2!=NULL)delete[]s2;
@ -677,6 +819,7 @@ int operator==(const RFunction& f1,const RFunction& f2)
if(f1.type!=f2.type)return 0; if(f1.type!=f2.type)return 0;
if(f1.type==-1)return 1; // Nonfunction==nonfunction if(f1.type==-1)return 1; // Nonfunction==nonfunction
if(f1.type==0)return (f1.pfuncval==f2.pfuncval&&EqStr(f1.name,f2.name)); if(f1.type==0)return (f1.pfuncval==f2.pfuncval&&EqStr(f1.name,f2.name));
if(f1.type==2)return (f1.pfuncobjval==f2.pfuncobjval&&EqStr(f1.name,f2.name));
if(f1.op!=f2.op)return 0; if(f1.op!=f2.op)return 0;
if(!EqStr(f1.name,f2.name))return 0; if(!EqStr(f1.name,f2.name))return 0;
if(f1.nvars!=f2.nvars)return 0; if(f1.nvars!=f2.nvars)return 0;
@ -761,7 +904,7 @@ signed char ROperation::HasError(const ROperation*pop) const
int ROperation::NMembers() const //Number of members for an operation like a,b,c... int ROperation::NMembers() const //Number of members for an operation like a,b,c...
{ {
if(op==Fun)return(pfunc->type==1?pfunc->op.NMembers():pfunc->type==0?1:0); if(op==Fun)return(pfunc->type==1?pfunc->op.NMembers():pfunc->type==0?1:pfunc->type==2?pfunc->nvars:0);
if(op!=Juxt)return 1;else if(mmb2==NULL)return 0;else return 1+mmb2->NMembers(); if(op!=Juxt)return 1;else if(mmb2==NULL)return 0;else return 1+mmb2->NMembers();
} }
@ -823,7 +966,7 @@ ROperation ROperation::Diff(const RVar& var) const
case Asin:return(mmb2->Diff(var)/sqrt(1-((*mmb2)^2))); case Asin:return(mmb2->Diff(var)/sqrt(1-((*mmb2)^2)));
case Acos:return(-mmb2->Diff(var)/sqrt(1-((*mmb2)^2))); case Acos:return(-mmb2->Diff(var)/sqrt(1-((*mmb2)^2)));
case Abs:return(mmb2->Diff(var)*(*mmb2)/(*this)); case Abs:return(mmb2->Diff(var)*(*mmb2)/(*this));
case Fun:if(pfunc->type==-1||pfunc->type==0)return ErrVal; case Fun:if(pfunc->type==-1||pfunc->type==0||pfunc->type==2)return ErrVal;
if(pfunc->nvars==0)return 0.; if(pfunc->nvars==0)return 0.;
else if(pfunc->op.NMembers()>1){ else if(pfunc->op.NMembers()>1){
j=pfunc->op.NMembers(); j=pfunc->op.NMembers();
@ -866,8 +1009,8 @@ char* ROperation::Expr() const
if(mmb2!=NULL){s2=mmb2->Expr();n+=strlen(s2);g=IsFunction(mmb2->op);} if(mmb2!=NULL){s2=mmb2->Expr();n+=strlen(s2);g=IsFunction(mmb2->op);}
s=new char[n]; s=new char[n];
switch(op){ switch(op){
case Num:return ValToStr(ValC); case Num:delete[]s;return ValToStr(ValC);
case Var:return CopyStr(pvar->name); case Var:delete[]s;return CopyStr(pvar->name);
case Juxt:sprintf(s,"%s , %s",s1,s2);break; case Juxt:sprintf(s,"%s , %s",s1,s2);break;
case Add: case Add:
f=f||(mmb1->op==Juxt); f=f||(mmb1->op==Juxt);
@ -982,7 +1125,7 @@ char* ROperation::Expr() const
case Fun: case Fun:
sprintf(s,"%s(%s)",pfunc->name,s2); sprintf(s,"%s(%s)",pfunc->name,s2);
break; break;
default:return CopyStr("Error"); default:delete[]s;return CopyStr("Error");
}; };
if(s1!=NULL)delete[] s1;if(s2!=NULL)delete[] s2; if(s1!=NULL)delete[] s1;if(s2!=NULL)delete[] s2;
return s; return s;
@ -1158,6 +1301,7 @@ void ROperation::BuildCode()
if(pvals!=NULL){delete[]pvals;pvals=NULL;}//does not delete pvals[0] in case it was to be deleted... (no way to know) if(pvals!=NULL){delete[]pvals;pvals=NULL;}//does not delete pvals[0] in case it was to be deleted... (no way to know)
if(ppile!=NULL){delete[]ppile;ppile=NULL;} if(ppile!=NULL){delete[]ppile;ppile=NULL;}
if(pfuncpile!=NULL){delete[]pfuncpile;pfuncpile=NULL;} if(pfuncpile!=NULL){delete[]pfuncpile;pfuncpile=NULL;}
switch(op){ switch(op){
case ErrOp:pinstr=new pfoncld[2];pinstr[0]=&NextVal;pinstr[1]=NULL; case ErrOp:pinstr=new pfoncld[2];pinstr[0]=&NextVal;pinstr[1]=NULL;
pvals=new double*[2];pvals[0]=new double(ErrVal);pvals[1]=NULL; pvals=new double*[2];pvals[0]=new double(ErrVal);pvals[1]=NULL;

View File

@ -1,6 +1,6 @@
/* /*
mathexpr.h version 2.0 mathexpr.h version 2.1
Copyright (c) 1997-2000 Yann OLLIVIER Copyright (c) 1997-2000 Yann OLLIVIER
@ -12,6 +12,12 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
*/ */
/* 2009 changes by Jean-Philippe Meuret <jpmeuret@free.fr> for Qatsh
- added EFunction class to support external function with any number of parameters
- ROperation::ROperation(const char*,int,PRVar*,int,PRFunction*) :
changed 1st arg from 'char*' to 'const char*' for GCC 4.2 (deprecated conversion)
*/
#ifndef _MATHEXPR_H #ifndef _MATHEXPR_H
#define _MATHEXPR_H #define _MATHEXPR_H
@ -79,7 +85,7 @@ class ROperation{
ROperation(const ROperation&); ROperation(const ROperation&);
ROperation(double); ROperation(double);
ROperation(const RVar&); ROperation(const RVar&);
ROperation(char*sp,int nvarp=0,PRVar*ppvarp=NULL,int nfuncp=0,PRFunction*ppfuncp=NULL); ROperation(const char*sp,int nvarp=0,PRVar*ppvarp=NULL,int nfuncp=0,PRFunction*ppfuncp=NULL);
~ROperation(); ~ROperation();
double Val() const; double Val() const;
signed char ContainVar(const RVar&) const; signed char ContainVar(const RVar&) const;
@ -114,15 +120,30 @@ class ROperation{
ROperation Substitute(const RVar&,const ROperation&) const; ROperation Substitute(const RVar&,const ROperation&) const;
}; };
class EFunction{
public:
unsigned nvars;
double* pvars;
EFunction(unsigned nvarsp);
virtual ~EFunction();
double operator()(double* pvarsp);
double operator()(double var1p, ...); // Warning: Only double vars supported
// Compute function value from pvars[] after operator () filled it
// => (re)implement only this function
virtual double Val() = 0;
};
class RFunction{ class RFunction{
double*buf; double*buf;
public: public:
signed char type; signed char type; //Compute the value through: 2=>pfuncobjval, 1=>op, 0=>pfuncval, -1=>Can't do it.
double ((*pfuncval)(double)); double ((*pfuncval)(double));
EFunction* pfuncobjval;
ROperation op;int nvars;RVar** ppvar; ROperation op;int nvars;RVar** ppvar;
char*name; char*name;
RFunction(); RFunction();
RFunction(double ((*)(double))); RFunction(double ((*)(double)));
RFunction(EFunction& efunc);
RFunction(const ROperation& opp,RVar* pvarp); RFunction(const ROperation& opp,RVar* pvarp);
RFunction(const ROperation& opp,int nvarsp,RVar**ppvarp); RFunction(const ROperation& opp,int nvarsp,RVar**ppvarp);
RFunction(const RFunction&); RFunction(const RFunction&);