symbexpr.inc 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. {
  2. $id: $
  3. Copyright (c) 2000 by Marco van de Voort ([email protected])
  4. member of the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright. (LGPL)
  7. TExpression class which does symbolic manipulation.
  8. Derivation routine based on 20 lines of conceptual pseudo code
  9. provided by Osmo Ronkanen.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. **********************************************************************
  14. Problems:
  15. - Often untested. I used my HP48g to check some complex derivatives,
  16. but more thorough checking and errorhandling is necessary.
  17. - RPN to Infix adds ()'s when not necessary. Should be made aware of precedence.
  18. (partially fixed)
  19. }
  20. {Should be moved to a math unit. Calculate x! with a 23 degree polynomal for
  21. ArbFloat values. From Numlib.
  22. Works for extended only. Redefine TC1 and TC3 for your numeric type
  23. if you want to use something else.}
  24. type Float10Arb =ARRAY[0..9] OF BYTE;
  25. const
  26. TC1 : Float10Arb = (0,0,$00,$00,$00,$00,0,128,192,63); {Eps}
  27. TC3 : Float10Arb = (1,0,0,0,0,0,0,0,0,0); {3.64519953188247460E-4951}
  28. var {Looks ugly, but is quite handy.}
  29. macheps : ArbFloat absolute TC1; { macheps = r - 1, with r
  30. the smallest ArbFloat > 1}
  31. midget : ArbFloat absolute TC3; { the smallest positive ArbFloat}
  32. function spepol(x: ArbFloat; var a: ArbFloat; n: ArbInt): ArbFloat;
  33. var pa : ^ArbFloat; {FPC extension. Uses ^ some array of ArbFloat in TP}
  34. i : ArbInt;
  35. polx : ArbFloat;
  36. begin
  37. pa:=@a;
  38. polx:=0;
  39. for i:=n downto 0 do
  40. polx:=polx*x+pa[i]; {and pa^[i] here}
  41. spepol:=polx
  42. end {spepol};
  43. function spegam(x: ArbFloat): ArbFloat;
  44. const
  45. tmax = 170;
  46. a: array[0..23] of ArbFloat =
  47. ( 8.86226925452758013e-1, 1.61691987244425092e-2,
  48. 1.03703363422075456e-1, -1.34118505705967765e-2,
  49. 9.04033494028101968e-3, -2.42259538436268176e-3,
  50. 9.15785997288933120e-4, -2.96890121633200000e-4,
  51. 1.00928148823365120e-4, -3.36375833240268800e-5,
  52. 1.12524642975590400e-5, -3.75499034136576000e-6,
  53. 1.25281466396672000e-6, -4.17808776355840000e-7,
  54. 1.39383522590720000e-7, -4.64774927155200000e-8,
  55. 1.53835215257600000e-8, -5.11961333760000000e-9,
  56. 1.82243164160000000e-9, -6.13513953280000000e-10,
  57. 1.27679856640000000e-10,-4.01499750400000000e-11,
  58. 4.26560716800000000e-11,-1.46381209600000000e-11);
  59. var tvsmall, t, g: ArbFloat;
  60. m, i: ArbInt;
  61. begin
  62. if sizeof(ArbFloat) = 6
  63. then
  64. tvsmall:=2*midget
  65. else
  66. tvsmall:=midget;
  67. t:=abs(x);
  68. if t > tmax
  69. then
  70. RunError(407);
  71. if t < macheps
  72. then
  73. begin
  74. if t < tvsmall
  75. then
  76. RunError(407);
  77. spegam:=1/x
  78. end
  79. else { abs(x) >= macheps }
  80. begin
  81. m:=trunc(x);
  82. if x > 0
  83. then
  84. begin
  85. t:=x-m; m:=m-1; g:=1;
  86. if m<0
  87. then
  88. g:=g/x
  89. else
  90. if m>0
  91. then
  92. for i:=1 to m do
  93. g:=(x-i)*g
  94. end
  95. else { x < 0 }
  96. begin
  97. t:=x-m+1;
  98. if t=1
  99. then
  100. RunError(407);
  101. m:=1-m;
  102. g:=x;
  103. for i:=1 to m do
  104. g:=(i+x)*g;
  105. g:=1/g
  106. end;
  107. spegam:=spepol(2*t-1, a[0], sizeof(a) div sizeof(ArbFloat) - 1)*g
  108. end { abs(x) >= macheps }
  109. end; {spegam}
  110. Procedure ExprInternalError(A,B:ArbInt);
  111. VAR S,S2 : String;
  112. begin
  113. Str(ORD(A),S);
  114. Str(ORD(B),S2);
  115. Raise EExprIE.Create(SExprIE+S+' '+S2);
  116. end;
  117. CONSTRUCTOR TExpression.Create(Infix:String);
  118. var dummy:String;
  119. begin
  120. ExprTree:=NIL;
  121. if (Infix<>'') then
  122. ExprTree:=InfixToParseTree(Infix,Dummy);
  123. InfixCache:=Infix;
  124. InfixClean:=True; {Current pnode status' infix
  125. representation is in infixcache}
  126. end;
  127. CONSTRUCTOR TExpression.EmptyCreate;
  128. begin
  129. ExprTree:=Nil;
  130. InfixClean:=false;
  131. end;
  132. Procedure TExpression.SetNewInfix(Infix:String);
  133. var dummy:String;
  134. begin
  135. if Assigned(ExprTree) Then
  136. Dispose(ExprTree);
  137. if infix<>'' then
  138. ExprTree:=InFixToParseTree(Infix,Dummy)
  139. else
  140. ExprTree:=NIL;
  141. InfixClean:=True;
  142. InfixCache:=Infix;
  143. end;
  144. Destructor TExpression.Destroy;
  145. begin
  146. If assigned(ExprTree) then
  147. DisposeExpr(ExprTree);
  148. inherited Destroy;
  149. end;
  150. function TExpression.GetRPN :String;
  151. begin
  152. if ExprTree=NIL Then
  153. Result:='0'
  154. else
  155. Result:=ParseTreeToRpn(ExprTree);
  156. end;
  157. function TExpression.GetInfix:String;
  158. begin
  159. if Not InfixClean then
  160. begin
  161. If ExprTree=NIL THEN
  162. InfixCache:='0'
  163. else
  164. InfixCache:=ParseTreeToInfix(ExprTree);
  165. InfixClean:=True;
  166. end;
  167. Result:=InfixCache;
  168. end;
  169. Function TExpression.GetIntValue:LongInt;
  170. begin
  171. SimplifyConstants;
  172. If ExprTree^.NodeType<>Iconstnode then
  173. Raise ENotInt.Create(SExprNotInt);
  174. result:=ExprTree^.ivalue;
  175. end;
  176. Procedure TExpression.SetIntValue(val:Longint);
  177. begin
  178. if ExprTree<> NIL then
  179. DisposeExpr(ExprTree);
  180. New(ExprTree);
  181. ExprTree^.NodeType:=iconstnode;
  182. ExprTree^.Ivalue:=Val;
  183. end;
  184. Function TExpression.GetFloatValue:ArbFloat;
  185. begin
  186. If ExprTree^.NodeType<>constnode then
  187. Raise ENotFloat.Create(SExprNotFloat);
  188. result:=ExprTree^.value;
  189. end;
  190. Procedure TExpression.SetFloatValue(val:ArbFloat);
  191. begin
  192. if ExprTree<> NIL then
  193. DisposeExpr(ExprTree);
  194. New(ExprTree);
  195. ExprTree^.NodeType:=constnode;
  196. ExprTree^.value:=Val;
  197. end;
  198. procedure TExpression.Simpleop(expr:TExpression;oper:calcop);
  199. begin
  200. exprtree:=NewCalc(oper,exprtree,CopyTree(expr.exprtree));
  201. InFixCache:='garbadge';
  202. InfixClean:=False;
  203. end;
  204. function TExpression.Simpleopwithresult(expr:TExpression;oper:calcop):TExpression;
  205. var tmp:pnode;
  206. begin
  207. result.EmptyCreate;
  208. result.SimplificationLevel:=simplificationlevel;
  209. result.exprtree:=NewCalc(Oper,CopyTree(ExprTree),CopyTree(Expr.ExprTree));
  210. end;
  211. procedure TExpression.Addto(Expr:TExpression);
  212. begin
  213. simpleop(expr,addo);
  214. end;
  215. procedure TExpression.SubFrom(Expr:TExpression);
  216. begin
  217. simpleop(expr,subo);
  218. end;
  219. procedure TExpression.Times(Expr:texpression);
  220. begin
  221. simpleop(expr,mulo);
  222. end;
  223. procedure TExpression.Divby(Expr:TExpression);
  224. begin
  225. simpleop(expr,dvdo);
  226. end;
  227. procedure TExpression.RaiseTo(Expr:TExpression);
  228. begin
  229. simpleop(expr,powo);
  230. end;
  231. function TExpression.add(Expr:TExpression):TExpression;
  232. begin
  233. result:=Simpleopwithresult(expr,addo);
  234. end;
  235. function TExpression.sub(Expr:TExpression):TExpression;
  236. begin
  237. result:=Simpleopwithresult(expr,subo);
  238. end;
  239. function TExpression.dvd(Expr:TExpression):TExpression;
  240. begin
  241. result:=Simpleopwithresult(expr,dvdo);
  242. end;
  243. function TExpression.mul(Expr:TExpression):TExpression;
  244. begin
  245. result:=Simpleopwithresult(expr,mulo);
  246. end;
  247. Function TExpression.IntDerive(const derivvariable:String;theexpr:pnode):pnode;
  248. function Deriv(t:pnode):pnode;
  249. {Derive subexpression T. Returns NIL if subexpression derives to 0, to avoid
  250. unnecessary (de)allocations. This is the reason why NewCalc is so big.}
  251. var x : ArbFloat;
  252. p1,p2 : pnode;
  253. begin
  254. Deriv:=nil;
  255. if (t=nil) then {Early out}
  256. exit;
  257. with t^ do begin
  258. case nodetype of
  259. VarNode: if upcase(variable)=derivvariable then
  260. Deriv:=NewiConst(ArbInt(1))
  261. else
  262. Deriv:=NIL;
  263. ConstNode : Deriv:=NIL;
  264. IConstNode: Deriv:=NIL;
  265. CalcNode: begin
  266. case op of
  267. addo,
  268. subo: Deriv:=NewCalc(op,Deriv(left),Deriv(right));
  269. mulo: Deriv:=NewCalc(addo,
  270. NewCalc(mulo,Deriv(left),copyTree(right)),
  271. NewCalc(mulo,Deriv(right),copytree(left)));
  272. dvdo: Deriv:=NewCalc(dvdo,
  273. NewCalc(subo,
  274. NewCalc(mulo,Deriv(left),copyTree(right)),
  275. NewCalc(mulo,Deriv(right),copytree(left))),
  276. NewFunc(sqrx,CopyTree(right)));
  277. powo: begin
  278. p1:=Deriv(Right);
  279. if P1<>NIL then
  280. p1:=NewCalc(mulo,p1,NewFunc(Lnx,CopyTree(Left))); { ln(l)*r'}
  281. p2:=Deriv(Left);
  282. if P2<>NIL then
  283. p2:=Newcalc(Mulo,CopyTree(Right),newcalc(mulo,p2,
  284. newfunc(invx,CopyTree(left))));
  285. IF (P1<>nil) and (p2<>nil) then
  286. deriv:=newcalc(mulo,CopyTree(t),newcalc(addo,p1,p2))
  287. else
  288. if (P1=NIL) and (P2=NIL) then {Simplify first to avoid this!}
  289. deriv:=NIL
  290. else
  291. begin
  292. if P1=NIL THEN { one of both is constant}
  293. P1:=P2; {The appopriate term is now in P1}
  294. deriv:=newcalc(mulo,CopyTree(t),p1);
  295. end;
  296. end;
  297. end;
  298. end;
  299. FuncNode: begin
  300. case fun of
  301. invx: Deriv:=NewCalc(dvdo,
  302. NewFunc(Minus,Deriv(son)),
  303. NewFunc(sqrx,CopyTree(son)));
  304. minus: Deriv:=NewFunc(minus,Deriv(son));
  305. sinx: Deriv:=NewCalc(Mulo,
  306. NewFunc(cosx,Copytree(son)),
  307. Deriv(son));
  308. cosx: deriv:=NewCalc(mulo,
  309. NewFunc(minus,NewFunc(sinx,copytree(son))),
  310. Deriv(son));
  311. tanx: deriv:=Newcalc(dvdo,deriv(son),
  312. newfunc(sqrx,newfunc(cosx,copytree(son))));
  313. sqrx: deriv:=newcalc(mulo, newiconst(2),
  314. newcalc(mulo,copytree(son),deriv(son)));
  315. { dx*1 /(2*sqrt(x)) }
  316. sqrtx: deriv:=newcalc(mulo, deriv(son),newcalc(dvdo,newiconst(1),
  317. newcalc(mulo,newiconst(2),newfunc(sqrtx,copytree(son)))));
  318. lnx : deriv:=newcalc(mulo,newcalc(dvdo,newiconst(1),CopyTree(son)),
  319. deriv(son)); { dln(x)=x' * 1/x}
  320. expx: deriv:=newcalc(mulo,newfunc(expx,copytree(son)),deriv(son));
  321. cotanx: deriv:=newfunc(minus,Newcalc(dvdo,deriv(son), { -dx/sqr(sin(x))}
  322. newfunc(sqrx,newfunc(sinx,copytree(son)))));
  323. coshx: deriv:=newcalc(mulo,newfunc(sinhx,copytree(son)),deriv(son));
  324. sinhx: deriv:=newcalc(mulo,newfunc(coshx,copytree(son)),deriv(son));
  325. arcsinhx, {according to HP48?}
  326. arcsinx: deriv:=newcalc(dvdo,deriv(son),newfunc(sqrtx,newcalc(subo,newiconst(1),
  327. newfunc(sqrx,copytree(son)))));
  328. arccosx: deriv:=newfunc(minus,newcalc(dvdo,deriv(son),
  329. newfunc(sqrtx,newcalc(subo,newiconst(1),newfunc(sqrx,copytree(son))))));
  330. arctanx: deriv:=newcalc(dvdo,deriv(son),newcalc(addo,newiconst(1),newfunc(sqrx,copytree(son))));
  331. log10x: deriv:=newcalc(mulo,newcalc(dvdo,newconst(0.434294481902),CopyTree(son)),
  332. deriv(son)); { dlog10(x)=x' * log10(e)/x}
  333. log2x: deriv:=newcalc(mulo,newcalc(dvdo,newconst(1.44269504089),CopyTree(son)),
  334. deriv(son)); { dlog2(x)=x' * log2(e)/x}
  335. stepx: ; {Should raise exception, not easily derivatable}
  336. tanhx: deriv:=newcalc(dvdo,deriv(son),newfunc(sqrx,newfunc(coshx,copytree(son))));
  337. arctanhx: deriv:=newcalc(dvdo,deriv(son),newfunc(sqrtx,newcalc(addo,newiconst(1),
  338. newfunc(sqrx,copytree(son)))));
  339. arccoshx: deriv:=NewCalc(dvdo,deriv(son),newcalc(mulo,newcalc(subo,newfunc(sqrtx,copytree(son)),newiconst(1)),
  340. newcalc(addo,newfunc(sqrtx,copytree(son)),newiconst(1))));
  341. lnxpix,arctan2x,
  342. hypotx,lognx : ; {Should also raise exceptions, not implemented yet}
  343. end;
  344. end;
  345. Func2Node: begin
  346. if son2left^.nodetype=constnode then
  347. x:=son2left^.value
  348. else
  349. x:=son2left^.ivalue;
  350. case fun of
  351. lognx: deriv:=newcalc(mulo,newcalc(dvdo,newconst(logn(x,2.71828182846)),
  352. CopyTree(son2right)),deriv(son2right));
  353. { dlogn(x)=x' * log(n,e)/x}
  354. Powerx: begin
  355. p1:=Deriv(Son2Right);
  356. if P1<>NIL then
  357. p1:=NewCalc(mulo,p1,NewFunc(Lnx,CopyTree(Son2Left))); { ln(l)*r'}
  358. p2:=Deriv(Son2Left);
  359. if P2<>NIL then
  360. p2:=Newcalc(Mulo,CopyTree(Son2Right),newcalc(mulo,p2,
  361. newfunc(invx,CopyTree(Son2Left))));
  362. IF (P1<>nil) and (p2<>nil) then
  363. deriv:=newcalc(mulo,CopyTree(t),newcalc(addo,p1,p2))
  364. else
  365. if (P1=NIL) and (P2=NIL) then {Simplify first to avoid this!}
  366. deriv:=NIL
  367. else
  368. begin
  369. if P1=NIL THEN { one of both is constant}
  370. P1:=P2; {The appopriate term is now in P1}
  371. deriv:=newcalc(mulo,CopyTree(t),p1);
  372. end;
  373. end;
  374. end;
  375. end;
  376. end;
  377. end; {WITH}
  378. end;
  379. begin
  380. Result:=Deriv(theexpr);
  381. end;
  382. function TExpression.power(Expr:TExpression):TExpression;
  383. begin
  384. result:=Simpleopwithresult(expr,powo);
  385. end;
  386. Function TExpression.Derive(derivvariable:String):TExpression;
  387. var tmpvar : Pnode;
  388. DerivObj: TExpression;
  389. begin
  390. derivvariable:=upcase(derivvariable);
  391. Tmpvar:=intDerive(derivvariable,exprtree);
  392. DerivObj:=TExpression.emptycreate;
  393. If tmpvar=NIL then
  394. derivobj.ExprTree:=NewIconst(0)
  395. else
  396. derivobj.exprtree:=tmpvar;
  397. derivobj.simplificationlevel:=simplificationlevel;
  398. DerivObj.InfixClean:=False;
  399. result:=derivobj;
  400. end;
  401. function ipower(x,y:ArbInt):ArbInt;
  402. var tmpval : ArbInt;
  403. begin
  404. if y<0 then
  405. ; {exception}
  406. if y=0 then
  407. result:=1
  408. else
  409. begin
  410. result:=x;
  411. if y<>1 then
  412. for tmpval:=2 to y do
  413. result:=result*x;
  414. end;
  415. end;
  416. function ifacul(x:ArbInt):ArbInt;
  417. var tmpval : ArbInt;
  418. begin
  419. if x<0 then
  420. ; {exception}
  421. if x=0 then
  422. result:=1
  423. else
  424. begin
  425. result:=1;
  426. if x<>1 then
  427. for tmpval:=2 to x do
  428. result:=result*tmpval;
  429. end;
  430. end;
  431. function EvaluateFunction(funcname:funcop;param:ArbFloat):ArbFloat;
  432. var Intermed : integer;
  433. begin
  434. case funcname of
  435. cosx : result:=Cos(param);
  436. sinx : result:=sin(param);
  437. tanx : result:=tan(param);
  438. sqrx : result:=sqr(param);
  439. sqrtx : result:=sqrt(param);
  440. expx : result:=exp(param);
  441. lnx : result:=ln(param);
  442. cotanx : result:=cotan(param);
  443. arcsinx : result:=arcsin(param);
  444. arccosx : result:=arccos(param);
  445. arctanx : result:=arctan(param);
  446. sinhx : result:=sinh(param);
  447. coshx : result:=cosh(param);
  448. tanhx : result:=tanh(param);
  449. arcsinhx : result:=arcsinh(param);
  450. arccoshx : result:=arccosh(param);
  451. arctanhx : result:=arctanh(param);
  452. log10x : result:=log10(param);
  453. log2x : result:=log2(param);
  454. lnxpix : result:=lnxp1(param);
  455. faculx : result:=spegam(param+1.0);
  456. else
  457. ExprInternalError(2,ord(funcname));
  458. end;
  459. If Result<1E-4900 then {Uncertainty in sinus(0.0)}
  460. Result:=0;
  461. end;
  462. procedure TExpression.SimplifyConstants;
  463. //procedure internalsimplify (expr:pnode;InCalc:Boolean;parent:pnode);
  464. procedure internalsimplify (expr:pnode);
  465. function isconst(p:pnode):boolean;
  466. begin
  467. isconst:=(p^.nodetype=iconstnode) or (p^.nodetype=constnode);
  468. end;
  469. function isconstnil(p:pnode):boolean;
  470. begin
  471. IsConstNil:=false;
  472. if (p^.nodetype=iconstnode) and (P^.ivalue=0) then
  473. IsConstNil:=True;
  474. If (p^.nodetype=constnode) and (P^.value=0) then
  475. IsConstNil:=True
  476. end;
  477. var val1,val2 : ArbFloat;
  478. ival1,
  479. ival2 : ArbInt;
  480. function setupoperation(operat:calcop;simlevel:longint;Postprocess:boolean;param2func:boolean):longint;
  481. function dosimple(mode:longint;theleft,theright:pnode):longint;
  482. begin
  483. If Mode >3 then
  484. ;
  485. result:=0;
  486. if mode=0 then
  487. exit;
  488. if (theright^.nodetype=iconstnode) and (theleft^.nodetype=iconstnode) then
  489. begin
  490. if mode=3 then
  491. begin
  492. result:=2;
  493. val2:=theright^.value;
  494. val1:=theleft^.value;
  495. end
  496. else
  497. begin
  498. result:=1;
  499. ival2:=theright^.ivalue;
  500. ival1:=theleft^.Ivalue;
  501. end;
  502. end;
  503. if (theright^.nodetype=constnode) and (theleft^.nodetype=constnode) then
  504. begin
  505. result:=2;
  506. val2:=theright^.value;
  507. val1:=theleft^.value;
  508. end;
  509. if mode>1 then
  510. begin
  511. if result=0 then
  512. begin
  513. if (theright^.nodetype=constnode) and (theleft^.nodetype=iconstnode) then
  514. begin
  515. result:=3;
  516. val2:=theright^.value;
  517. val1:=theleft^.ivalue;
  518. end;
  519. if (theright^.nodetype=iconstnode) and (theleft^.nodetype=constnode) then
  520. begin
  521. result:=4;
  522. val2:=theright^.ivalue;
  523. val1:=theleft^.value;
  524. end;
  525. end;
  526. end;
  527. end;
  528. begin
  529. Result:=0;
  530. if SimplificationLevel<>0 then
  531. if param2func then
  532. result:=DoSimple(SimLevel,expr^.son2left,expr^.son2right)
  533. else
  534. result:=DoSimple(SimLevel,expr^.left,expr^.right);
  535. with expr^ do
  536. begin
  537. IF (result>0) and PostProcess then
  538. begin
  539. if (operat<>dvdo) then { Divide is special case. If
  540. integer x/y produces a fraction
  541. we want to be able to roll back}
  542. begin
  543. if Param2func then
  544. begin
  545. dispose(son2right);
  546. dispose(son2left);
  547. end
  548. else
  549. begin
  550. dispose(right);
  551. dispose(left);
  552. end;
  553. if result=1 then
  554. nodetype:=iconstnode
  555. else
  556. nodetype:=constnode;
  557. flags:=[ExprIsConstant];
  558. end;
  559. end;
  560. end;
  561. end;
  562. procedure Checkvarnode(p:pnode);
  563. var treal:arbfloat;
  564. error:integer;
  565. tint :Integer;
  566. begin
  567. TrimLeft(P^.variable);
  568. TrimRight(p^.variable);
  569. Val(p^.variable, treal, Error);
  570. IF (error=0) then {Conversion to real succeeds. Numeric}
  571. begin
  572. p^.flags:=[ExprIsConstant];
  573. if (Pos('.',p^.variable)=0) and (Pos('E',p^.variable)=0) Then
  574. begin
  575. Val(p^.variable,tint,Error);
  576. If error=0 then
  577. begin
  578. p^.nodetype:=iconstnode;
  579. p^.ivalue:=tint;
  580. end
  581. else
  582. begin
  583. p^.nodetype:=constnode;
  584. p^.value:=treal;
  585. end;
  586. end
  587. else
  588. begin
  589. p^.nodetype:=constnode;
  590. p^.value:=treal;
  591. end;
  592. end;
  593. end;
  594. var tmpval : ArbInt;
  595. invdummy: pnode;
  596. begin
  597. case expr^.nodetype of
  598. VarNode : CheckVarnode(expr); {sometimes a numeric value can slip in as constant.
  599. (e.g. as people pass it as symbol to taylor or
  600. "subst" methods}
  601. calcnode : begin
  602. //If not
  603. internalsimplify(expr^.left); {Reduce left and right as much as possible}
  604. internalsimplify(expr^.right);
  605. if isconst(expr^.left) and isconst(expr^.right) then
  606. begin
  607. TmpVal:=Setupoperation(expr^.op,SimplificationLevel,true,false);
  608. if tmpval>0 then
  609. with expr^ do
  610. case op of
  611. addo :
  612. if tmpval=1 then
  613. ivalue:=ival1+ival2
  614. else
  615. value:=val1+val2;
  616. subo : if tmpval=1 then
  617. ivalue:=ival1-ival2
  618. else
  619. value:=val1-val2;
  620. mulo : if tmpval=1 then
  621. ivalue:=ival1*ival2
  622. else
  623. value:=val1*val2;
  624. dvdo : begin
  625. if tmpval=1 then
  626. begin
  627. tmpval:=ival1 div ival2;
  628. if (tmpval*ival2)=ival1 then {no rounding, OK!}
  629. begin
  630. Dispose(expr^.right);
  631. Dispose(Expr^.left);
  632. nodetype:=iconstnode;
  633. ivalue:=tmpval;
  634. end; {ELSE do nothing}
  635. end
  636. else
  637. begin
  638. dispose(expr^.right);
  639. dispose(expr^.left);
  640. nodetype:=constnode;
  641. value:=val1 / val2;
  642. end;
  643. flags:=[ExprIsConstant];
  644. end;
  645. powo : If tmpval=1 then
  646. begin
  647. if ival2<0 then {integer x^-y -> inv (x^y)}
  648. begin
  649. expr^.nodetype:=funcnode;
  650. expr^.son:=NewIConst(IPower(Ival1,-Ival2));
  651. end
  652. else
  653. ivalue:=ipower(ival1,ival2);
  654. end
  655. else
  656. value:=exp(val2*ln(val1));
  657. else
  658. ExprInternalError(1,ord(Expr^.op));
  659. end; {case}
  660. end {if}
  661. else {At least one node is symbolic, or both types are wrong}
  662. begin
  663. With Expr^ do
  664. if IsConstNil(Left) then
  665. begin
  666. Dispose(Left);
  667. case op of
  668. addo : begin
  669. InvDummy:=Right;
  670. Expr^:=Right^;
  671. Dispose(InvDummy);
  672. end;
  673. subo: begin
  674. invdummy:=right;
  675. NodeType:=funcNode;
  676. Fun:=Minus;
  677. son:=invdummy;
  678. Flags:=Son^.Flags;
  679. end;
  680. mulo,powo,dvdo : begin
  681. Dispose(Right);
  682. nodetype:=IconstNode;
  683. ivalue:=0;
  684. Flags:=[ExprIsConstant];
  685. end;
  686. end;
  687. end
  688. else
  689. if IsConstNil(Right) then
  690. begin
  691. if expr^.op<>dvdo then {Leave tree for DVD intact because of exception}
  692. Dispose(Right);
  693. case expr^.op of
  694. addo,subo : begin
  695. InvDummy:=left;
  696. Expr^:=left^;
  697. Dispose(InvDummy);
  698. end;
  699. mulo : begin
  700. Dispose(Left);
  701. nodetype:=IconstNode;
  702. Flags:=[ExprIsConstant];
  703. ivalue:=0;
  704. end;
  705. powo : begin
  706. Dispose(Left);
  707. nodetype:=IconstNode;
  708. Flags:=[ExprIsConstant];
  709. ivalue:=1;
  710. end;
  711. dvdo : Raise EDiv0.Create(SExprInvSimp);
  712. else
  713. ExprInternalError(6,ord(Expr^.op));
  714. end;
  715. end;
  716. end;
  717. With Expr^ Do
  718. Begin
  719. IF (nodetype=calcnode) and (Op in [Mulo,Addo]) then
  720. begin {Commutative operator rearrangements, move constants to left}
  721. if (ExprIsConstant IN Right^.flags) and NOT (ExprIsConstant IN Left^.flags) then
  722. begin
  723. InvDummy:=Right;
  724. Right:=Left;
  725. Left:=InvDummy;
  726. end;
  727. IF (right^.nodetype=calcnode) and (right^.Op in [Mulo,Addo]) then
  728. begin
  729. end;
  730. end;
  731. End;
  732. end; {case calcnode}
  733. funcnode: begin
  734. internalSimplify(expr^.son);
  735. Case Expr^.fun of
  736. Minus : if IsConst(expr^.son) then
  737. begin
  738. InvDummy:=Expr^.Son;
  739. expr^:=InvDummy^;
  740. if InvDummy^.Nodetype=IconstNode then
  741. expr^.ivalue:=-expr^.ivalue
  742. else
  743. expr^.value:=-expr^.value;
  744. dispose(InvDummy);
  745. end;
  746. invx : begin
  747. InvDummy:=Expr^.son;
  748. If InvDummy^.nodeType=ConstNode Then
  749. begin
  750. if InvDummy^.Value=0.0 then
  751. Raise EDiv0.Create(SExprInvMsg);
  752. Expr^.NodeType:=ConstNode;
  753. Expr^.Value:=1/InvDummy^.Value;
  754. Dispose(InvDummy);
  755. end
  756. else
  757. if InvDummy^.nodetype=iconstnode then
  758. begin
  759. if InvDummy^.iValue=0 then
  760. Raise EDiv0.Create(SExprinvmsg);
  761. If (InvDummy^.iValue=1) or (InvDummy^.iValue=-1) then
  762. begin
  763. expr^.NodeType:=Iconstnode;
  764. Expr^.iValue:=InvDummy^.iValue;
  765. Dispose(InvDummy);
  766. end;
  767. end;
  768. end;
  769. else {IE check in EvaluateFunction}
  770. if (expr^.son^.nodetype=constnode) and (Expr^.fun<>faculx) then {Other functions, only func(real) is simplified}
  771. begin
  772. val1:=EvaluateFunction(expr^.fun,Expr^.son^.value);
  773. dispose(expr^.son);
  774. expr^.nodetype:=constnode;
  775. expr^.value:=val1;
  776. end;
  777. end; {Case 2}
  778. end;
  779. Func2Node : begin
  780. internalSimplify(expr^.son2left);
  781. internalSimplify(expr^.son2right);
  782. case expr^.fun2 of
  783. powerx : begin
  784. TmpVal:=Setupoperation(powo,SimplificationLevel,true,true);
  785. if TmpVal>1 then
  786. begin
  787. If tmpval=1 then
  788. begin
  789. if ival2<0 then {integer x^-y -> inv (x^y)}
  790. begin
  791. new(invdummy);
  792. invdummy^.nodetype:=iconstnode;
  793. invdummy^.ivalue:=ipower(ival1,-ival2);
  794. expr^.nodetype:=funcnode;
  795. expr^.son:=invdummy;
  796. end
  797. else
  798. expr^.ivalue:=ipower(ival1,ival2);
  799. end;
  800. end;
  801. end;
  802. stepx : begin
  803. {N/I yet}
  804. end;
  805. arctan2x : begin
  806. TmpVal:=Setupoperation(powo,SimplificationLevel,false,true);
  807. if tmpval>1 then {1 is integer, which we don't do}
  808. begin
  809. dispose(expr^.right);
  810. dispose(expr^.left);
  811. expr^.nodetype:=constnode;
  812. expr^.value:=arctan2(ival2,ival1);
  813. end;
  814. end;
  815. hypotx :begin
  816. TmpVal:=Setupoperation(powo,SimplificationLevel,false,true);
  817. if tmpval>1 then {1 is integer, which we don't do}
  818. begin
  819. dispose(expr^.right);
  820. dispose(expr^.left);
  821. expr^.nodetype:=constnode;
  822. expr^.value:=hypot(ival2,ival1);
  823. end;
  824. end;
  825. lognx: begin
  826. TmpVal:=Setupoperation(powo,SimplificationLevel,false,true);
  827. if tmpval>1 then {1 is integer, which we don't do}
  828. begin
  829. dispose(expr^.right);
  830. dispose(expr^.left);
  831. expr^.nodetype:=constnode;
  832. expr^.value:=hypot(ival2,ival1);
  833. end;
  834. end;
  835. else
  836. ExprInternalError(3,ORD(expr^.fun2));
  837. end;
  838. end;
  839. { else
  840. ExprInternalError(4,ORD(expr^.nodetype));}
  841. end; {Case 1}
  842. end;
  843. begin
  844. internalsimplify(exprtree);
  845. InfixClean:=False; {Maybe changed}
  846. end;
  847. procedure TExpression.SymbolSubst(ToSubst,SubstWith:String);
  848. procedure InternalSubst(expr:Pnode);
  849. begin
  850. if Expr<>NIL THEN
  851. case Expr^.NodeType of
  852. VarNode: if Expr^.Variable=ToSubst then
  853. Expr^.Variable:=SubstWith;
  854. calcnode: begin
  855. InternalSubst(Expr^.left);
  856. InternalSubst(Expr^.right);
  857. end;
  858. funcnode: InternalSubst(Expr^.son);
  859. func2node: begin
  860. InternalSubst(Expr^.son2left);
  861. InternalSubst(Expr^.son2right);
  862. end;
  863. end;
  864. end;
  865. begin
  866. InternalSubst(ExprTree);
  867. end;
  868. function TExpression.SymbolicValueNames:TStringList;
  869. var TheList:TStringList;
  870. procedure InternalSearch(expr:Pnode);
  871. begin
  872. if Expr<>NIL THEN {NIL shouldn't be allowed, and signals corruption. IE? Let it die?}
  873. case Expr^.NodeType of
  874. VarNode: begin
  875. Expr^.Variable:=UpCase(Expr^.Variable);
  876. TheList.Add(Expr^.Variable);
  877. end;
  878. calcnode: begin
  879. InternalSearch(Expr^.left);
  880. InternalSearch(Expr^.right);
  881. end;
  882. funcnode: InternalSearch(Expr^.son);
  883. func2node: begin
  884. InternalSearch(Expr^.son2left);
  885. InternalSearch(Expr^.son2right);
  886. end;
  887. end;
  888. end;
  889. begin
  890. TheList:=TStringList.Create;
  891. TheList.Sorted:=TRUE;
  892. Thelist.Duplicates:=DupIgnore;
  893. InternalSearch(ExprTree);
  894. Result:=TheList;
  895. end;
  896. function TExpression.Taylor(Degree:ArbInt;const x,x0:String):TExpression;
  897. {Taylor(x,x0)=sum(m,0,degree, d(f)/d(x))(x0)/ m! * (x-x0)^m)
  898. = f(x0)+ (x-x0)/1! * df/d(x) + (x-x0)^2 / 2! * d^2(f)/d^2(x) +
  899. (x-x0)^3 / 3! * d^3(f)/d^3(x) + ....
  900. }
  901. Var TaylorPol : TExpression; { The result expression}
  902. Root,
  903. Tmp,Tmp2,
  904. tmp3,tmp4,tmp5 : pnode; { Always have a nice storage of pointers.
  905. Used to hold all intermediate results}
  906. I,facul : ArbInt; { Loop counters and faculty term}
  907. begin
  908. TaylorPol:=TExpression.EmptyCreate; {New expression}
  909. TaylorPol.ExprTree:=CopyTree(ExprTree); {make a copy of the parsetree}
  910. TaylorPol.SymbolSubst(X,X0); {subst x by x0. All occurances
  911. of f() refer to x0, not x}
  912. if Degree>0 then {First term only? Or nonsense (negative?)}
  913. {Then ready. 0th term only.}
  914. begin {Start preparing easy creation of higher terms}
  915. tmp2:=newcalc(subo,newvar(x),
  916. newvar(x0)); {tmp2= x-x0 needed separately for first term}
  917. tmp4:=Newiconst(5); {exponent for x-x0, "a" need to keep a reference}
  918. tmp3:=newcalc(powo,tmp2,tmp4); {tmp3= (x-x0)^a}
  919. tmp5:=newiconst(5); {faculty value, "b"=m! also keep a reference for later modification }
  920. tmp3:=Newcalc(dvdo,tmp3,tmp5); {tmp3= (x-x0)^a / b a and b can be changed}
  921. facul:=1; {Calculate faculty as we go along. Start with 1!=1}
  922. root:=TaylorPol.ExprTree; {0th term}
  923. tmp:=root; {term that needs to be differentiated per iteration}
  924. for i:=1 to Degree do
  925. begin
  926. facul:=Facul*i; {Next faculty value 1!*1 =1 btw :_)}
  927. tmp:=intderive(x0,tmp); {Differentiate f^n(x0) to f^(n+1)(x0)}
  928. If I=1 then {first term is special case. No power }
  929. tmp2:=NewCalc(mulo,CopyTree(tmp2),tmp) {or faculty needed (^1 and 1!)}
  930. else
  931. begin
  932. tmp5^.Ivalue:=facul; {Higher terms. Set faculty}
  933. tmp4^.ivalue:=i; {Set exponent (=a) of (x-x0)^a}
  934. tmp2:=NewCalc(mulo,CopyTree(tmp3),tmp); {multiplicate derivative with (x-x0)^a/b term}
  935. end;
  936. root:=NewCalc(addo,root,tmp2); {String all terms together}
  937. end;
  938. DisposeExpr(tmp3); {Is only CopyTree()'d, not in new expression}
  939. TaylorPol.Exprtree:=root; {Set result}
  940. end;
  941. Result:=TaylorPol;
  942. end;
  943. function TExpression.Newton(x:String):TExpression;
  944. {
  945. f(x)
  946. Newton(x)=x- ----
  947. f'(x)
  948. }
  949. Var NewtonExpr : TExpression; { The result expression}
  950. Root,
  951. Tmp,Tmp2,
  952. tmp3,tmp4,tmp5 : pnode; { Always have a nice storage of pointers.
  953. Used to hold all intermediate results}
  954. I,facul : ArbInt; { Loop counters and faculty term}
  955. begin
  956. NewtonExpr:=TExpression.EmptyCreate; {New expression}
  957. {Should I test for constant expr here?}
  958. Tmp:=CopyTree(ExprTree); {make a copy of the parsetree
  959. for the f(x) term}
  960. Tmp2:=intDerive(x,tmp); { calc f'(x)}
  961. Tmp3:=NewVar(x); { Create (x)}
  962. Tmp:=Newcalc(dvdo,tmp,tmp2); { f(x)/f'(x)}
  963. tmp:=Newcalc(subo,tmp3,tmp); { x- f(x)/f'(x)}
  964. {We built the above expression using a copy of the tree.
  965. So no pointers into self.exprtree exist. We can now safely assign it
  966. to the new exprtree}
  967. NewtonExpr.ExprTree:=tmp;
  968. NewtonExpr.SimplifyConstants; {Simplify if f'(x)=constant, and
  969. kill 0*y(x) terms}
  970. Result:=NewtonExpr;
  971. end;
  972. {
  973. $Log$
  974. Revision 1.1 2002/12/15 21:01:26 marco
  975. Initial revision
  976. }
  977.