symbolic.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. unit Symbolic;
  2. {
  3. $ id: $
  4. Copyright (c) 2000 by Marco van de Voort([email protected])
  5. member of the Free Pascal development team
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright. (LGPL)
  8. Base types for expression trees, and some small procs to create them.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. Seems not to have memory leaks atm. If you experience them, check procedure
  13. newcalc first.
  14. }
  15. interface
  16. {$ifdef FPC}
  17. {$Mode ObjFpc}
  18. {$ENDIF}
  19. Uses Math,Classes,Sysutils;
  20. Const
  21. VLIWIncr = 40; { Initial size and increment of VLIW array}
  22. DelphiMaxOps = 5000; { Unused for FPC. Max records in VLIW array
  23. FPC: 2 Gb/sizeof(vliwevalword).}
  24. Type {Should be somewhere in the JCLMath or even in JCLtypes unit}
  25. {$ifdef FPC}
  26. ArbFloat = float; {Float is set to mathtype used by FPC Math unit}
  27. ArbInt = longint;
  28. {$else}
  29. ArbFloat = extended;
  30. ArbInt = Integer;
  31. {$endif}
  32. calcop=(addo,subo,mulo,dvdo,powo); {real operators}
  33. FuncOp=(cosx,sinx,tanx,sqrx,sqrtx,expx,lnx,invx,minus,cotanx,arcsinx,arccosx,
  34. arctanx,sinhx,coshx,tanhx,arcsinhx,arccoshx,arctanhx,log10x,
  35. log2x,lnxpix,faculx,arctan2x,stepx,powerx,hypotx,lognx,unknown0,
  36. unknown1,unknown2,unknown3,unknown4);
  37. {functions, both one and two parameter ones. Including pseudo function
  38. minus}
  39. CONST UnknownTokens : array[0..4] OF FuncOp =(unknown0,unknown1,unknown2,
  40. unknown3,unknown4);
  41. TYPE
  42. Operation=(VarNode,ConstNode,iconstnode,CalcNode,FuncNode,func2node,VLIWVar,CustomNode);
  43. TFlagsEnum=(ExprIsConstant); {ExprIsConstant signals that this node of
  44. the tree and deeper can evaluate to a single
  45. float constant}
  46. TFlags = SET OF TFlagsEnum;
  47. pnode =^treenode;
  48. treenode=record
  49. Flags : TFlags;
  50. case nodetype:operation of
  51. iconstnode: (ivalue:ArbInt);
  52. VarNode: (variable:string[11]);
  53. VLIWVar: (vliwindex:ArbInt); {^float?}
  54. ConstNode: (value:ArbFloat);
  55. CalcNode: (op:calcop;left,right:pnode);
  56. FuncNode: (fun:funcop;son:pnode);
  57. Func2Node: (fun2:funcop;son2left,son2right:pnode);
  58. CustomNode: (Indent:Longint);
  59. end;
  60. ERPNStack = Class(Exception); {All RPN stack problems category}
  61. EIError = Class(Exception); {All internal errors. Most often
  62. these are raised when unknown
  63. function enumerations are found}
  64. EDiv0 = Class(Exception); {Division by zero, but RPN, not processor!}
  65. TBaseExpression = class
  66. protected
  67. ExprTree : pnode;
  68. function NewConst(value:ArbFloat):pnode;
  69. function NewiConst(value:ArbInt):pnode;
  70. function NewCalc(op:calcop;left,right:pnode):pnode;
  71. function CopyTree(p :pnode):pnode;
  72. function NewFunc(fun:funcop;son:pnode):pnode; overload;
  73. function NewFunc(fun:funcop;son,son2:pnode):pnode; overload;
  74. function NewVar(variable:string):pnode;
  75. procedure DisposeExpr(p:pnode);
  76. end;
  77. EParserStack = class(ERPNStack); {RPN stack under/overflow.}
  78. EParserIE = class(EIError); {Internal error}
  79. TBaseExprParser= class(TBaseExpression)
  80. public
  81. function InFixToParseTree(Expr : String;VAR RPNexpr: String):pnode; virtual;
  82. function ParseTreeToRPN (expr:pnode):string; virtual;
  83. function ParseTreeToInfix(expr:pnode):string; virtual;
  84. end;
  85. TEvaluator= CLASS;
  86. EFaculNotInt = Class(exception); {Faculty on a real value deviating from an integer value by more than 0.01}
  87. EExprIE = Class(EIerror);
  88. ENotInt = Class(exception);
  89. ENotFloat = Class(Exception);
  90. TExpression = class(TBaseExprParser)
  91. protected
  92. InfixClean : Boolean;
  93. InfixCache : String;
  94. Evaluator : TEvaluator;
  95. EvaluatorUpToDate : Boolean;
  96. function GetInfix:String;
  97. function GetRPN:String;
  98. procedure Simpleop(expr:TExpression;oper:calcop);
  99. function Simpleopwithresult(expr:TExpression;oper:calcop):TExpression;
  100. Function IntDerive(const derivvariable:String;theexpr:pnode):pnode;
  101. Function GetIntValue:LongInt;
  102. Procedure SetIntValue(val:Longint);
  103. Function GetFloatValue:ArbFloat;
  104. Procedure SetFloatValue(val:ArbFloat);
  105. Procedure UpdateConstants; {Kind of integrity check}
  106. public
  107. SimplificationLevel : Longint;
  108. CONSTRUCTOR Create(Infix:String);
  109. CONSTRUCTOR EmptyCreate;
  110. DESTRUCTOR Destroy; override;
  111. Procedure SetNewInfix(Infix:String);
  112. Function Derive(derivvariable:String):TExpression;
  113. procedure SymbolSubst(ToSubst,SubstWith:String);
  114. function SymbolicValueNames:TStringList;
  115. function Taylor(Degree:ArbInt;const x,x0:String):TExpression;
  116. function Newton(x:String):TExpression;
  117. procedure SimplifyConstants;
  118. function add(Expr:TExpression):TExpression;
  119. function dvd(Expr:TExpression):TExpression;
  120. function mul(Expr:TExpression):TExpression;
  121. function power(Expr:TExpression):TExpression;
  122. function sub(Expr:TExpression):TExpression;
  123. procedure Addto(Expr:TExpression);
  124. procedure Divby(Expr:TExpression);
  125. procedure RaiseTo(Expr:TExpression);
  126. procedure SubFrom(Expr:TExpression);
  127. procedure Times(Expr:texpression);
  128. property InfixExpr: string read GetInfix write SetNewInfix;
  129. property RpnExpr: string read GetRPN;
  130. property ValueAsInteger:longint read GetIntValue write SetIntvalue; {Default?}
  131. property ValueAsFloat:arbfloat read GetFloatValue write SetFloatValue;
  132. end;
  133. VLIWWordtype= (avariable,anoperation, afunction,
  134. afconstant, aiconstant,placeholder);
  135. { RPN operators or functions with two arguments are the same.}
  136. vliwop2=(addv,subv,mulv,dvdv,powv,arctan2v,stepv,hypotv,lognv);
  137. pArbFloat = ^ArbFloat;
  138. {$ifdef FPC}
  139. pVliwArr = ^VLIWEvalWord;
  140. {$else} {Delphi doesn't allow referencing of ^simpletype as array,
  141. but does allow it for ^ array of VLIWEvalWord}
  142. TVLIWArr = array[0..DelphiMaxOps] of VLiwEvalWord;
  143. pVliwArr = ^TVliwArr;
  144. {$ENDIF}
  145. pVLIWEvalWord = ^VLIWEvalWord;
  146. VLIWEvalword = record
  147. case VLIWEntity : VLIWWordType OF
  148. AVariable : (IndexOfVar : ArbInt);
  149. AnOperation: (op:vliwop2); {2 arguments}
  150. AFunction : (fun1:funcop); {functions with one argument}
  151. AiConstant : (ivalue:ArbInt);
  152. AfConstant : (value:ArbFloat);
  153. placeholder: (IndexOfConstant:ArbInt) ;
  154. end;
  155. TEvaluatorNotEnoughVariables=class(Exception); {Not enough variables passed to Evaluate}
  156. TEvaluatorStackException =class(ERPNStack); {RPN Stack over/under flow}
  157. TEvaluatorBadConstant =class(Exception); {Constant value not specified}
  158. TEvaluatorIE =class(Exception); {Internal error. Probably something out of sync.}
  159. TEvaluator = Class {Only needs the notion of a pnode }
  160. Private
  161. VariableName : TStringList;
  162. ConstantValue : TList;
  163. ConstantNames : TStringList;
  164. MaxStack,
  165. VLIWCount,
  166. VLIWAlloc : ArbInt;
  167. VLIWRPNExpr : pVLIWArr;
  168. public
  169. function Evaldepth:longint;
  170. PROCEDURE SetConstant(Name:String;Value:ArbFloat);
  171. CONSTRUCTOR Create(VariableList:TStringList;Expression:pnode);
  172. CONSTRUCTOR Create(VariableList:TStringList;Expression:TExpression);
  173. DESTRUCTOR Destroy; override;
  174. Procedure TreeToVLIWRPN(expr:pnode);
  175. function Evaluate(const variables:Array of ArbFloat):ArbFloat;
  176. {$IFDEF DebugDump}
  177. procedure debugger;
  178. procedure WriteVliw(p:VLIWEvalWord);
  179. {$ENDIF}
  180. end;
  181. {
  182. Structures used to index a pnode tree to identify terms.
  183. PTerms = ^TTerms;
  184. PtermNode=^TTermNode;
  185. TtermNode= record
  186. NrTerms :ArbInt;
  187. Terms : Array[0..499] of PNode;
  188. end;
  189. TTerms = record
  190. NrTerms : ArbInt;
  191. Terms: Array[0..499] of PtermNode;
  192. end;
  193. }
  194. const InfixOperatorName : array[addo..powo] of char= ('+','-','*','/','^');
  195. FunctionNames : array[cosx..lognx] of string[8]=(
  196. 'cos','sin','tan','sqr','sqrt','exp','ln','inv','-',
  197. 'cotan','arcsin','arccos','arctan','sinh',
  198. 'cosh','tanh','arcsinh','arccosh','arctanh',
  199. 'log10','log2','lnxp1','!','arctan2',
  200. 'step','power','hypot','logn');
  201. FunctionNamesUpper: array[cosx..lognx] of string[8]=(
  202. 'COS','SIN','TAN','SQR','SQRT','EXP','LN','INV','-',
  203. 'COTAN','ARCSIN','ARCCOS','ARCTAN','SINH',
  204. 'COSH','TANH','ARCSINH','ARCCOSH','ARCTANH',
  205. 'LOG10','LOG2','LNXP1','!','ARCTAN2',
  206. 'STEP','POWER','HYPOT','LOGN');
  207. LenFunctionNames : array[cosx..lognx] of longint=
  208. (3,3,3,3,3,3,2,3,1,5,6,6,6,4,4,4,7,7,7,5,4,5,1,7,4,5,5,4);
  209. {$I exprstrs.inc}
  210. implementation
  211. {newconst and newiconst are overloaded in FPC}
  212. function TBaseExpression.NewConst(value:ArbFloat):pnode;
  213. {Generate a new node for a floating point constant}
  214. var t : pnode;
  215. begin
  216. new(t);
  217. t^.nodetype:=constnode;
  218. t^.value:=value;
  219. t^.Flags:=[ExprIsConstant];
  220. NewConst:=T;
  221. end;
  222. function TBaseExpression.NewiConst(value:ArbInt):pnode;
  223. {Generate a new node for integer constant}
  224. var t : pnode;
  225. begin
  226. new(t);
  227. t^.nodetype:=iconstnode;
  228. t^.ivalue:=value;
  229. t^.Flags:=[ExprIsConstant];
  230. NewiConst:=T;
  231. end;
  232. procedure TBaseExpression.DisposeExpr(p:pnode);
  233. {Recursively kill expression tree}
  234. begin
  235. IF p<>NIL THEN
  236. begin
  237. case p^.nodetype of
  238. CalcNode : begin
  239. DisposeExpr(p^.right);
  240. DisposeExpr(p^.left);
  241. end;
  242. FuncNode : DisposeExpr(p^.son);
  243. end;
  244. Dispose(p);
  245. end;
  246. end;
  247. function TBaseExpression.NewCalc(op:calcop;left,right:pnode):pnode;
  248. {Create NewCalc node. Left and Right may be nil because
  249. to avoid introducing empty nodes, the deriv()
  250. function may return NIL's, which are to be treated as newiconst(0);
  251. Also one of the functions most likely to have memory leaks
  252. }
  253. function isintegerone(testme:pnode) : boolean;
  254. begin
  255. Isintegerone:=(testme^.nodetype=iconstnode) and (testme^.ivalue=1);
  256. end;
  257. var t : pnode;
  258. begin
  259. if op=powo then
  260. begin
  261. if right=NIL then {x^0 =1 for every X}
  262. begin
  263. DisposeExpr(left);
  264. newcalc:=newiconst(1);
  265. exit;
  266. end;
  267. if left=NIL THEN { 0^y = 0 except for y=0, but that is}
  268. begin { covered above}
  269. DisposeExpr(right);
  270. NewCalc:=NIL;
  271. exit;
  272. end;
  273. if IsIntegerone(left) then {x^1 =x}
  274. begin
  275. DisposeExpr(left);
  276. NewCalc:=right;
  277. exit;
  278. end;
  279. If IsIntegerone(right) then { 1^y=1}
  280. begin
  281. DisposeExpr(left);
  282. NewCalc:=right;
  283. exit;
  284. end;
  285. end; {generate a plain power node for all other cases}
  286. if left=NIL then
  287. begin
  288. if (right=nil) or (op=mulo) or (op=dvdo) then { 0*0, 0*t or 0/t =0}
  289. begin { We have no way to check T for nul}
  290. IF Right<>NIL then
  291. DisposeExpr(Right);
  292. NewCalc:=NIL;
  293. exit;
  294. end;
  295. if op=addo then { Don't generate a calc node for 0+x, but return x}
  296. begin
  297. NewCalc:=right;
  298. exit;
  299. end;
  300. new(t);
  301. t^.nodetype:=funcnode; { 0-x = minus(x) }
  302. t^.fun:=minus;
  303. t^.son:=right;
  304. t^.flags:=[];
  305. if ExprIsConstant in t^.son^.flags then
  306. include(t^.flags,ExprIsConstant);
  307. NewCalc:=T;
  308. exit;
  309. end;
  310. if right=NIL then
  311. begin
  312. if (left=nil) or (op=mulo) or (op=dvdo) then { 0*0, 0*t or 0/t =0}
  313. begin
  314. IF left<>NIL then
  315. disposeExpr(Left);
  316. NewCalc:=Nil;
  317. exit;
  318. end;
  319. NewCalc:=Left; { for x-0 or x+0, simply return 0}
  320. exit;
  321. end;
  322. If ((op=mulo) or (op=dvdo)) and isintegerone(right) then { simplify t*1 and t/1}
  323. begin
  324. DisposeExpr(right);
  325. NewCalc:=Left;
  326. exit;
  327. end;
  328. if (op=mulo) and isintegerone(left) then { simplify 1*t}
  329. begin
  330. DisposeExpr(left);
  331. NewCalc:=right;
  332. exit;
  333. end;
  334. new(t);
  335. t^.nodetype:=calcnode;
  336. t^.op:=op;
  337. t^.left:=left;
  338. t^.right:=right;
  339. t^.Flags:=[];
  340. if (ExprIsConstant In T^.Left^.Flags) and (ExprIsConstant In T^.Right^.Flags) then
  341. include(t^.flags,ExprIsConstant);
  342. newcalc:=t;
  343. end;
  344. function TBaseExpression.CopyTree(p :pnode):pnode;
  345. var newtree : pnode;
  346. begin
  347. new(newtree);
  348. move(p^,Newtree^,sizeof(treenode));
  349. if newtree^.nodetype=CalcNode then
  350. begin
  351. newtree^.left:=CopyTree(p^.left);
  352. newtree^.right:=CopyTree(p^.right);
  353. end
  354. else
  355. if newtree^.nodetype=FuncNode then
  356. newtree^.son:=CopyTree(p^.son);
  357. CopyTree:=NewTree;
  358. end;
  359. function TBaseExpression.NewFunc(fun:funcop;son:pnode):pnode;
  360. var t : pnode;
  361. begin
  362. IF son<>nil then
  363. begin
  364. new(t);
  365. t^.nodetype:=funcnode;
  366. t^.fun:=fun;
  367. t^.son:=son;
  368. t^.flags:=[];
  369. if ExprIsConstant IN son^.flags then
  370. Include(t^.flags,ExprIsConstant);
  371. NewFunc:=T;
  372. end
  373. else
  374. NewFunc:=NIL;
  375. end;
  376. function TBaseExpression.NewFunc(fun:funcop;son,son2:pnode):pnode;
  377. var t : pnode;
  378. begin
  379. new(t);
  380. t^.nodetype:=func2node;
  381. t^.fun:=fun;
  382. t^.son2Left:=son;
  383. t^.son2Right:=son2;
  384. t^.flags:=[];
  385. if(ExprIsConstant IN son^.flags) and (ExprIsConstant IN son2^.flags) then
  386. Include(t^.flags,ExprIsConstant);
  387. NewFunc:=T;
  388. end;
  389. {function TBaseExpression.NewFunc(fun:funcop;unknownIdent:longint):pnode;
  390. var t : pnode;
  391. begin
  392. new(t);
  393. t^.nodetype:=func2node;
  394. t^.fun:=fun;
  395. t^.son2Left:=son;
  396. t^.son2Right:=son2;
  397. t^.flags:=[];
  398. if(ExprIsConstant IN son^.flags) and (ExprIsConstant IN son2^.flags) then
  399. Include(t^.flags,ExprIsConstant);
  400. NewFunc:=T;
  401. end;}
  402. function TBaseExpression.NewVar(variable:string):pnode;
  403. var p :pnode;
  404. begin
  405. new(p);
  406. p^.nodetype:=varnode;
  407. p^.variable:=variable;
  408. p^.Flags:=[];
  409. newvar:=p;
  410. end;
  411. {$I parsexpr.inc} {Parser categories}
  412. {$I symbexpr.inc} {standard symbolic manip}
  413. {$I teval.inc}
  414. {$I rearrang.inc}
  415. end.
  416. {
  417. $Log$
  418. Revision 1.1 2002/12/15 21:01:26 marco
  419. Initial revision
  420. }