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
@ -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)
{
@ -27,10 +53,12 @@ char* MidStr(const char*s,int i1,int i2)
s1[i2-i1+1]=0;return s1;
}
// Return a copy of the given string (use strdup ?)
char* CopyStr(const char*s)
{char*s1=new char[strlen(s)+1];char*s12=s1;const char*s2=s;
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
{if(n<0||n>(int)strlen(s))return;
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;
}
// Tell if s[n...] == 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;
int i;
@ -72,7 +101,49 @@ RVar::RVar(const char*namep,double*pvalp)
{pval=pvalp;name=CopyStr(namep);}
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()
{
@ -86,11 +157,18 @@ RFunction::RFunction(double ((*pfuncvalp)(double)))
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)
{
if(this==&rfunc)return;
type=rfunc.type;op=rfunc.op;
pfuncval=rfunc.pfuncval;
pfuncobjval=rfunc.pfuncobjval;
name=CopyStr(rfunc.name);
nvars=rfunc.nvars;
if(rfunc.ppvar!=NULL&&nvars){
@ -129,6 +207,7 @@ RFunction& RFunction::operator=(const RFunction& rfunc)
if(this==&rfunc)return *this;
type=rfunc.type;op=rfunc.op;
pfuncval=rfunc.pfuncval;
pfuncobjval=rfunc.pfuncobjval;
delete[]name;
name=CopyStr(rfunc.name);
if(ppvar!=NULL)delete[]ppvar;
@ -150,6 +229,7 @@ double RFunction::Val(double x) const
{
if(type==-1||nvars>=2)return ErrVal;
if(type==0)return (*pfuncval)(x);
if(type==2)return (*pfuncobjval)(x);
double xb=*(*ppvar)->pval,y;
*(*ppvar)->pval=x; // Warning : could cause trouble if this value is used in a parallel process
y=op.Val();
@ -161,6 +241,7 @@ double RFunction::Val(double*pv) const
{
if(type==-1)return ErrVal;
if(type==0)return (*pfuncval)(*pv);
if(type==2)return (*pfuncobjval)(pv);
double y;
int i;
for(i=0;i<nvars;i++){
@ -311,8 +392,8 @@ ROperation resultat;
resultat.op=Pow;resultat.mmb1=new ROperation(op1);
resultat.mmb2=new ROperation(op2);
return resultat;
}
}
ROperation sqrt(const ROperation& op)
{ROperation rop;rop.op=Sqrt;rop.mmb2=new ROperation(op);return rop;}
ROperation abs(const ROperation& op)
@ -344,8 +425,8 @@ ROperation ApplyOperator(int n,ROperation**pops,ROperation (*func)(const ROperat
ROperation RFunction::operator()(const ROperation& op)
{
/* Code to use to replace explcitly instead of using a pointer to
if(nvars!=op.NMembers()||type==-1||type==0)return ErrVal;
/* Code to use to replace explicitly instead of using a pointer to
if(nvars!=op.NMembers()||type==-1||type==0||type==2)return ErrVal;
ROperation op2=*pop;int i;
RVar**ppvar2=new PRVar[nvars];char s[11]="";
for(i=0;i<nvars;i++){
@ -364,24 +445,29 @@ ROperation RFunction::operator()(const ROperation& op)
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;
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)
{if(c!='0'&&c!='1'&&c!='2'&&c!='3'&&c!='4'
&&c!='5'&&c!='6'&&c!='7'&&c!='8'&&c!='9'&&c!='.')return 0;
return 1;
{
if((c<'0'||c>'9')&&c!='.')
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)
{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
{if(n>=(int)strlen(s)-1)return -1;
int i,c=1;
@ -392,6 +478,7 @@ for(i=n+1;s[i];i++){
return -1;
}
//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;
int i,c=1;
@ -414,22 +501,26 @@ int SearchOperator(char*s,ROperator op)
case Div:opc='/';break;
case Pow:opc='^';break;
case NthRoot:opc='#';break;
case E10:opc='E';break;
case E10:opc='E';break;
default:return -1;
};
int 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;};
};
return -1;
}
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;
if(s1[0]=='('&&SearchCorOpenbracket(s1,0)==s2-s1-1){
s1++;s2--;ind=1;}
// Return an empty string if nothing remains
if(s1==s2)
{
delete[]s;
@ -437,7 +528,9 @@ if(s1==s2)
s[0]=0;
return;
}
// Remove any leading spaces
if(s1[0]==' '){ind=1;while(s1[0]==' '&&s1<s2)s1++;}
// Return an empty string if nothing remains
if(s1==s2)
{
delete[]s;
@ -445,14 +538,19 @@ if(s1==s2)
s[0]=0;
return;
}
// Remove any ending spaces
if(*(s2-1)==' '){ind=1;while(s2>s1&&*(s2-1)==' ')s2--;}
*s2=0;
// Replace input string by simplified one
s1=CopyStr(s1);delete[]s;s=s1;
// Continue simplification (recursive) if needed
if(ind)SimplifyStr(s);
}
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)
{
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;
}
// Detect the name of a standard function, and return its length
int IsFunction(const char*s,int n)
{
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;
}
// 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)
//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);
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));
return l;
}
@ -492,49 +593,68 @@ signed char IsFunction(ROperator op)
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
{
int i,j;
i=0;
for(i=0;s[i];i++){
// Jump over a (...) bloc if any
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))){
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
{
int i,i2,ind=0,t1,t2;
int i,i2=0,ind=0,t1,t2;
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;};
// Jump over a variable or a function name if any
t1=IsVar(s,i,nvar,ppvar);t2=IsFunction(s,i,nfunc,ppfunc);
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;};
// Here, we detect the beginning of a number
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,')');
}
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;
int i,j,k,l;signed char flag=1;
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;}
while(s[0]==':'||s[0]==';'){
s1=CopyStr(s+1);delete[]s;s=s1;s1=NULL;
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(EqStr(s,"pi")||EqStr(s,"PI")||EqStr(s,"Pi"))
{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))
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;};
// Case 3: Something else.
for(k=0;s[k];k++){
// Jump over a (...) block.
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)){
i=k+l;while(s[i]==' ')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;}
}
}
// 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);
if(nvar)IsolateVars(s,nvar,ppvarp,nfuncp,ppfuncp);
SupprSpaces(s);
// Case 3a: "," binary operator (Juxtaposition)
i=SearchOperator(s,Juxt);
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);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Case 3b: "+" binary operator (Addition)
i=SearchOperator(s,Add);
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);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Case 3c: "-" binary operator (Substraction)
i=SearchOperator(s,Sub);
if(i!=-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);
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);
op=Opp;mmb1=NULL;
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Enclose function "calls" between parenthesis
for(i=0;s[i];i++){
// Jump over a (...) block.
if(s[i]=='('){i=SearchCorOpenbracket(s,i);if(i==-1)break;continue;};
// Detecting a function "call" :
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]==';'){
// s=DelStr(s,k);
j=k;while(s[j]!='(')j++;
j=SearchCorOpenbracket(s,j);
if(j!=-1){InsStr(s,j,')');InsStr(s,i,'(');i=j+2;}
// ??? => enclose it between parenthesis (=> "(???)" )
}else if(s[k]==':'){
// s=DelStr(s,k);
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]=='(')
InsStr(s,++i,'*');
// Case 3e: Standard or user defined function
if(s[0]=='('&&SearchCorOpenbracket(s,0)==(int)strlen(s)-1){
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);}
@ -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;}
goto fin;
};
// Case 3f: "*" binary operator (Multiplication)
i=SearchOperator(s,Mult);
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);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Case 3g: "/" binary operator (Division)
i=SearchOperator(s,Div);
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);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Case 3h: "^" binary operator (Power)
i=SearchOperator(s,Pow);
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);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Case 3i: "#" binary operator (Square or N>2 th root)
i=SearchOperator(s,NthRoot);
if(i!=-1){s1=MidStr(s,0,i-1);s2=MidStr(s,i+1,strlen(s)-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);};
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Case 3k: "E" binary operator (power of 10)
i=SearchOperator(s,E10);
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);
mmb2=new ROperation(s2,nvar,ppvarp,nfuncp,ppfuncp);goto fin;
};
// Other cases: Error.
op=ErrOp;mmb1=NULL;mmb2=NULL;
//
fin:
BuildCode();
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==-1)return 1; // Nonfunction==nonfunction
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(!EqStr(f1.name,f2.name))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...
{
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();
}
@ -823,7 +966,7 @@ ROperation ROperation::Diff(const RVar& var) const
case Asin: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 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.;
else if(pfunc->op.NMembers()>1){
j=pfunc->op.NMembers();
@ -832,7 +975,7 @@ ROperation ROperation::Diff(const RVar& var) const
op2=ApplyOperator(pfunc->nvars,ppop1,&operator,);
for(i=0;i<pfunc->nvars;i++)delete ppop1[i];
delete[]ppop1;
return op2;
return op2;
}else{
ppop1=new ROperation*[pfunc->nvars];
for(i=0;i<pfunc->nvars;i++){
@ -844,7 +987,7 @@ ROperation ROperation::Diff(const RVar& var) const
op2=ApplyOperator(pfunc->nvars,ppop1,&::operator+);
for(i=0;i<pfunc->nvars;i++)delete ppop1[i];
delete[]ppop1;
return op2;
return op2;
// In the obtained expression, f' will have been replaced with its expression but f will remain pointing to itself ; this could cause some trouble if changing f afterwards
}
default:return ErrVal;
@ -866,8 +1009,8 @@ char* ROperation::Expr() const
if(mmb2!=NULL){s2=mmb2->Expr();n+=strlen(s2);g=IsFunction(mmb2->op);}
s=new char[n];
switch(op){
case Num:return ValToStr(ValC);
case Var:return CopyStr(pvar->name);
case Num:delete[]s;return ValToStr(ValC);
case Var:delete[]s;return CopyStr(pvar->name);
case Juxt:sprintf(s,"%s , %s",s1,s2);break;
case Add:
f=f||(mmb1->op==Juxt);
@ -982,7 +1125,7 @@ char* ROperation::Expr() const
case Fun:
sprintf(s,"%s(%s)",pfunc->name,s2);
break;
default:return CopyStr("Error");
default:delete[]s;return CopyStr("Error");
};
if(s1!=NULL)delete[] s1;if(s2!=NULL)delete[] s2;
return s;
@ -1063,7 +1206,7 @@ double ROperation::Val() const
{
pfoncld*p1=pinstr;double**p2=pvals,*p3=ppile-1;PRFunction*p4=pfuncpile;
for(;*p1!=NULL;p1++)
if(*p1==&NextVal)*(++p3)=**(p2++);else
if(*p1==&NextVal)*(++p3)=**(p2++);else
if(*p1==&RFunc) ApplyRFunc(*(p4++),p3);
else (**p1)(p3);
return *p3;
@ -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(ppile!=NULL){delete[]ppile;ppile=NULL;}
if(pfuncpile!=NULL){delete[]pfuncpile;pfuncpile=NULL;}
switch(op){
case ErrOp:pinstr=new pfoncld[2];pinstr[0]=&NextVal;pinstr[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
@ -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
#define _MATHEXPR_H
@ -79,7 +85,7 @@ class ROperation{
ROperation(const ROperation&);
ROperation(double);
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();
double Val() const;
signed char ContainVar(const RVar&) const;
@ -114,15 +120,30 @@ class ROperation{
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{
double*buf;
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));
EFunction* pfuncobjval;
ROperation op;int nvars;RVar** ppvar;
char*name;
RFunction();
RFunction(double ((*)(double)));
RFunction(EFunction& efunc);
RFunction(const ROperation& opp,RVar* pvarp);
RFunction(const ROperation& opp,int nvarsp,RVar**ppvarp);
RFunction(const RFunction&);