solve.red 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  1. module solve; % Solve one or more algebraic equations.
  2. % Author: David R. Stoutemyer.
  3. % Major modifications by: Anthony C. Hearn and Donald R. Morrison.
  4. create!-package('(solve ppsoln glsolve solvealg solvetab quartic),nil);
  5. % Other packages needed by solve package.
  6. load!-package 'matrix;
  7. fluid '(!*allbranch !*exp !*ezgcd !*limitedfactors !*multiplicities
  8. !*nonlnr !*notseparate !*numval !*numval!* !*rounded
  9. !*solvealgp !*solvesingular !!gcd !:prec!: asymplis!* dmode!*);
  10. global '(!!arbint !*micro!-version multiplicities!*);
  11. switch allbranch,multiplicities,nonlnr,solvesingular;
  12. !*nonlnr := t; % Put it on for now.
  13. flag('(!*allbranch multiplicities!*),'share);
  14. % ***** Some Non-local variables *****
  15. !*allbranch := t; % Returns all branches of solutions if T.
  16. % !*multiplicities Lists all roots with multiplicities if on.
  17. !*solvesingular := t; % Default value.
  18. % !!gcd SOLVECOEFF returns GCD of powers of its arg in
  19. % this. With the decompose code, this should
  20. % only occur with expressions of form x^n + c.
  21. algebraic operator arbint,arbreal;
  22. % algebraic operator arbcomplex;
  23. % Done this way since it's also defined in the glmat module.
  24. deflist('((arbcomplex simpiden)),'simpfn);
  25. % ***** Utility Functions *****
  26. symbolic procedure freeofl(u,v);
  27. null v or freeof(u,car v) and freeofl(u,cdr v);
  28. symbolic procedure ratnump x;
  29. % Returns T iff any prefix expression x is a rational number.
  30. atom numr(x := simp!* x) and atom denr x;
  31. flag ('(ratnump), 'boolean);
  32. symbolic procedure allkern elst;
  33. % Returns list of all top-level kernels in the list of standard
  34. % forms elst.
  35. if null elst then nil
  36. else union(kernels car numr elst, allkern cdr elst);
  37. symbolic procedure topkern(u,x);
  38. % Returns list of top level kernels in the standard form u that
  39. % contain the kernel x;
  40. for each j in kernels u conc if not freeof(j,x) then list j else nil;
  41. symbolic procedure coeflis ex;
  42. % Ex is a standard form. Returns a list of the coefficients of the
  43. % main variable in ex in the form ((expon . coeff) (expon . coeff)
  44. % ... ), where the expon's occur in increasing order, and entries do
  45. % not occur of zero coefficients. We need to reorder coefficients
  46. % since kernel order can change in the calling function.
  47. begin scalar ans,var;
  48. if domainp ex then return (0 . ex);
  49. var := mvar ex;
  50. while not domainp ex and mvar ex=var do
  51. <<ans := (ldeg ex . reorder lc ex) . ans; ex := red ex>>;
  52. if ex then ans := (0 . reorder ex) . ans;
  53. return ans
  54. end;
  55. % ***** Evaluation Interface *****
  56. symbolic procedure solveeval u;
  57. begin scalar !!gcd; integer nargs;
  58. if atom u then rerror(solve,1,"SOLVE called with no equations");
  59. nargs := length u;
  60. u := if nargs=1 then solve0(car u,nil)
  61. else if nargs=2 then solve0(car u, cadr u)
  62. else solve0(car u,'list . cdr u);
  63. return !*solvelist2solveeqlist u
  64. end;
  65. put('solve,'psopfn,'solveeval);
  66. symbolic procedure !*solvelist2solveeqlist u;
  67. begin scalar x,y,z;
  68. for each j in u do
  69. <<if caddr j=0 then rerror(solve,2,"zero multiplicity")
  70. else if null cadr j
  71. then x := for each k in car j collect
  72. list('equal,!*q2a k,0)
  73. else x := for each k in pair(cadr j,car j)
  74. collect list('equal,car k,!*q2a cdr k);
  75. if length x > 1 then x := 'list . x else x := car x;
  76. if !*multiplicities then x := nlist(x,caddr j)
  77. else <<x := list x; y := caddr j . y>>;
  78. z := nconc!*(x,z)>>;
  79. if !*multiplicities then multiplicities!* := nil
  80. else multiplicities!* := 'list . y;
  81. return 'list . z
  82. end;
  83. % ***** Fundamental SOLVE Procedures *****
  84. Comment most of these procedures return a list of "solve solutions". A
  85. solve solution is a list with three fields: the list of solutions,
  86. the corresponding variables (or NIL if the equations could not be
  87. solved --- in which case there is only one solution in the first
  88. field) and the multiplicity;
  89. symbolic procedure solve0(elst,xlst);
  90. % This is the driving function for the solve package.
  91. % Elst is any prefix expression, including a list prefixed by LIST.
  92. % Xlst is a kernel or list of kernels. Solves eqns in elst for
  93. % vars in xlst, returning either a list of solutions, or a single
  94. % solution.
  95. begin scalar !*exp,!*notseparate,vars,w; integer neqn;
  96. !*exp := !*notseparate := t;
  97. % Form a list of equations as expressions.
  98. elst := for each j in solveargchk elst collect simp!* !*eqn2a j;
  99. neqn := length elst; % There must be at least one.
  100. % Determine variables.
  101. if null xlst
  102. then <<vars := allkern elst;
  103. terpri();
  104. if null vars then nil
  105. else if cdr vars
  106. then <<prin2!* "Unknowns: "; maprin('list . vars)>>
  107. else <<prin2!* "Unknown: "; maprin car vars>>;
  108. terpri!* nil>>
  109. else <<xlst := solveargchk xlst;
  110. vars := for each j in xlst collect !*a2k j>>;
  111. if length vars = 0
  112. then rerror(solve,3,"SOLVE called with no variables")
  113. else if neqn = 1
  114. then if null numr car elst
  115. then return if !*solvesingular
  116. then list list(for each j in vars
  117. collect !*f2q makearbcomplex(),
  118. vars,1)
  119. else nil
  120. else if length vars=1
  121. then if solutionp(w := solvesq(car elst,car vars,1))
  122. or null !*solvealgp
  123. or univariatep numr car elst
  124. then return w;
  125. % More than one equation or variable, or single eqn has no solution.
  126. elst := for each j in elst collect numr j;
  127. w := solvesys(elst,vars);
  128. if car w eq 't or car w eq 'inconsistent then return cdr w
  129. else if car w eq 'failed or null car w
  130. then return for each j in elst collect list(list(j ./ 1),nil,1)
  131. else errach list("Improper solve solution tag",car w)
  132. end;
  133. symbolic procedure solutionp u;
  134. null u or cadar u;
  135. symbolic procedure solveargchk u;
  136. if getrtype (u := reval u) eq 'list then cdr reval u
  137. else if atom u or not(car u eq 'lst) then list u
  138. else cdr u;
  139. % ***** Procedures for solving a single eqn *****
  140. symbolic procedure solvesq (ex,var,mul);
  141. % Attempts to find solutions for standard quotient ex with respect to
  142. % top level occurrences of var and kernels containing variable var.
  143. % Solutions containing more than one such kernel are returned
  144. % unsolved, and solve1 is applied to the other solutions. Integer
  145. % mul is the multiplicity passed from any previous factorizations.
  146. % Returns a list of triplets consisting of solutions, variables and
  147. % multiplicity.
  148. begin scalar !*ezgcd,e1,x1,y,z; integer mu;
  149. ex := numr ex;
  150. if null topkern(ex,var) then return nil;
  151. if null !*limitedfactors and null dmode!* then !*ezgcd := t;
  152. ex := fctrf ex;
  153. % Now process monomial.
  154. if domainp car ex then ex := cdr ex
  155. else ex := (car ex . 1) . cdr ex;
  156. for each j in ex do
  157. <<e1 := car j;
  158. x1 := topkern(e1,var);
  159. mu := mul*cdr j;
  160. % Test for decomposition of e1.
  161. if length x1=1
  162. and length(y := decomposef1(e1,nil))>1
  163. and (y := solvedecomp(reverse y,car x1,mu))
  164. then z := append(y,z)
  165. else if x1
  166. then z := append(
  167. if null cdr x1 then solve1(e1,car x1,var,mu)
  168. else if (y := principal!-of!-powers!-soln(e1,x1,var,mu))
  169. neq 'unsolved
  170. then y
  171. else if not smemq('sol,
  172. (x1:=simp!* list('sol,mk!*sq(e1 ./ 1), var)))
  173. then solvesq(x1,var,mu)
  174. else list list(list(e1 ./ 1),nil,mu),
  175. z)>>;
  176. return z
  177. end;
  178. symbolic procedure solvedecomp(u,var,mu);
  179. % Solve for decomposed expression. At the moment, only one
  180. % level of decomposition is considered.
  181. begin scalar failed,x;
  182. if length(x := solve0(car u,cadadr u))=1 then return nil;
  183. u := cdr u;
  184. while u do
  185. <<x := for each j in x conc
  186. if caddr j neq 1 or null cadr j
  187. then <<lprim list("Tell Hearn solvedecomp",x,u);
  188. failed := t;
  189. nil>>
  190. else solve0(list('difference,prepsq caar j,caddar u),
  191. if cdr u then cadadr u else var);
  192. if failed then u := nil else u := cdr u>>;
  193. return if failed then nil else adjustmul(x,mu)
  194. end;
  195. symbolic procedure adjustmul(u,n);
  196. % Multiply the multiplicities of the solutions in u by n.
  197. if n=1 then u
  198. else for each x in u collect list(car x,cadr x,n*caddr x);
  199. symbolic procedure solve1(e1,x1,var,mu);
  200. Comment e1 is a standard form, non-trivial in the kernel x1, which
  201. is itself a function of var, mu is an integer. Uses roots of
  202. unity, known solutions, inverses, together with quadratic, cubic
  203. and quartic formulas, treating other cases as unsolvable.
  204. Returns a list of solve solutions;
  205. begin scalar !*numval!*;
  206. !*numval!* := !*numval; % Keep value for use in solve11.
  207. return solve11(e1,x1,var,mu)
  208. end;
  209. symbolic procedure solve11(e1,x1,var,mu);
  210. begin scalar !*numval,b,coefs,hipow; integer n;
  211. !*numval := t; % Assume that actual numerical values wanted.
  212. coefs:= errorset!*(list('solvecoeff,mkquote e1,mkquote x1),nil);
  213. if atom coefs then return list list(list(e1 . 1),nil,mu);
  214. % solvecoeff problem - no soln.
  215. coefs := car coefs;
  216. n:= !!gcd; % numerical gcd of powers.
  217. hipow := caar reverse coefs;
  218. if hipow = 1
  219. then return begin scalar lincoeff,y,z;
  220. if null cdr coefs then b := 0
  221. else b := prepsq quotsq(negsq cdar coefs,cdadr coefs);
  222. if n neq 1 then b := list('expt,b,list('quotient,1,n));
  223. % We may need to merge more solutions in the following if
  224. % there are repeated roots.
  225. for k := 0:n-1 do % equation in power of var.
  226. <<lincoeff := simp!* list('times,b,
  227. mkexp list('quotient,list('times,k,2,'pi),n));
  228. if x1=var
  229. then y := solnmerge(list lincoeff,list var,mu,y)
  230. else if not idp(z := car x1)
  231. then typerr(z,"solve operator")
  232. else if z := get(z,'solvefn)
  233. then y := append(apply1(z,list(cdr x1,var,mu,lincoeff))
  234. ,y)
  235. else if (z := get(car x1,'inverse)) % known inverse
  236. then y := append(solvesq(subtrsq(simp!* cadr x1,
  237. simp!* list(z,mk!*sq lincoeff)),
  238. var,mu),y)
  239. else y := list(list subtrsq(simp!* x1,lincoeff),nil,mu)
  240. . y>>;
  241. return y
  242. end
  243. else if hipow=2
  244. then return <<x1 := exptsq(simp!* x1,n);
  245. % allows for power variable
  246. for each j in solvequadratic(getcoeff(coefs,2),
  247. getcoeff(coefs,1),getcoeff(coefs,0))
  248. conc solvesq(subtrsq(x1,j),var,mu)>>
  249. else return solvehipow(e1,x1,var,mu,coefs,hipow)
  250. end;
  251. symbolic procedure getcoeff(u,n);
  252. % Get the nth coefficient in the list u as a standard quotient.
  253. if null u then nil ./ 1
  254. else if n=caar u then cdar u
  255. else if n<caar u then nil ./ 1
  256. else getcoeff(cdr u,n);
  257. symbolic procedure putcoeff(u,n,v);
  258. % Replace the nth coefficient in the list u by v.
  259. if null u then list(n . v)
  260. else if n=caar u then (n . v) . cdr u
  261. else if n<caar u then (n . v) . u
  262. else car u . putcoeff(cdr u,n,v);
  263. symbolic procedure solvehipow(e1,x1,var,mu,coefs,hipow);
  264. % Solve a system with degree greater than 2. Since we cannot write
  265. % down the solution directly, we look for various forms that we
  266. % know how to solve.
  267. begin scalar b,c,d,f,rcoeffs;
  268. f:=(hipow+1)/2;
  269. d:=exptsq(simp!* x1,!!gcd);
  270. rcoeffs := reverse coefs;
  271. return if solve1test1(coefs,rcoeffs,f) % Coefficients symmetric.
  272. then if f+f=hipow+1 % odd
  273. then <<c:=addsq(d, 1 ./ 1);
  274. append(solvesq(c,var,mu),
  275. solvesq(quotsq(e1 ./ 1, c),var,mu))>>
  276. else <<coefs := putcoeff(coefs,0,2 ./ 1);
  277. coefs := putcoeff(coefs,1,simp!* '!!x);
  278. c:=addsq(multsq(getcoeff(coefs,f+1),
  279. getcoeff(coefs,1)),
  280. getcoeff(coefs,f));
  281. for j:=2:f do <<
  282. coefs := putcoeff(coefs,j,
  283. subtrsq(multsq(getcoeff(coefs,1),
  284. getcoeff(coefs,j-1)),
  285. getcoeff(coefs,j-2)));
  286. c:=addsq(c,multsq(getcoeff(coefs,j),
  287. getcoeff(coefs,f+j)))>>;
  288. for each j in solvesq(c,'!!x,mu) conc
  289. solvesq(addsq(1 ./ 1,multsq(d,subtrsq(d,caar j))),
  290. var,caddr j)>>
  291. else if solve1test2(coefs,rcoeffs,f)
  292. % coefficients antisymmetric
  293. then <<c:=addsq(d,(-1 ./1));
  294. b := solvesq(c,var,mu);
  295. e1 := quotsq(e1 ./ 1, c);
  296. if f+f = hipow
  297. then <<c := addsq(d,(1 ./ 1));
  298. b := append(solvesq(c,var,mu),b);
  299. e1 := quotsq(e1,c)>>;
  300. append(solvesq(e1,var,mu),b)>>
  301. % equation has no symmetry
  302. % now look for real roots before cubics or quartics. We must
  303. % reverse the answer from solveroots so that roots come out
  304. % in same order from SOLVE.
  305. % else if !*numval!* and (!*float or !*bigfloat) and univariatep e1
  306. else if !*numval!* and !*rounded and univariatep e1
  307. then reversip solveroots(e1,var,mu)
  308. else if hipow=3 and null !*micro!-version
  309. then for each j in solvecubic(getcoeff(coefs,3),
  310. getcoeff(coefs,2),
  311. getcoeff(coefs,1),
  312. getcoeff(coefs,0))
  313. conc solvesq(subtrsq(d,j),var,mu)
  314. else if hipow=4 and null !*micro!-version
  315. then for each j in solvequartic(getcoeff(coefs,4),
  316. getcoeff(coefs,3),
  317. getcoeff(coefs,2),
  318. getcoeff(coefs,1),
  319. getcoeff(coefs,0))
  320. conc solvesq(subtrsq(d,j),var,mu)
  321. else list list(list(e1 ./ 1),nil,mu)
  322. % We can't solve quintic and higher.
  323. end;
  324. symbolic procedure solnmerge(u,varlist,mu,y);
  325. % Merge solutions in case of multiplicities. It may be that this is
  326. % only needed for the trivial solution x=0.
  327. if null y then list list(u,varlist,mu)
  328. else if u = caar y and varlist = cadar y
  329. then list(caar y,cadar y,mu+caddar y) . cdr y
  330. else car y . solnmerge(u,varlist,mu,cdr y);
  331. symbolic procedure nilchk u; if null u then !*f2q u else u;
  332. symbolic procedure solve1test1(coefs,rcoeffs,f);
  333. % True if equation is symmetric in its coefficients. f is midpoint.
  334. begin integer j,p;
  335. if null coefs or caar coefs neq 0 then return nil;
  336. p := caar coefs + caar rcoeffs;
  337. a: if j>f then return t
  338. else if (caar coefs + caar rcoeffs) neq p
  339. or cdar coefs neq cdar rcoeffs then return nil;
  340. coefs := cdr coefs;
  341. rcoeffs := cdr rcoeffs;
  342. j := j+1;
  343. go to a
  344. end;
  345. symbolic procedure solve1test2(coefs,rcoeffs,f);
  346. % True if equation is antisymmetric in its coefficients. f is
  347. % midpoint.
  348. begin integer j,p;
  349. if null coefs or caar coefs neq 0 then return nil;
  350. p := caar coefs + caar rcoeffs;
  351. a: if j>f then return t
  352. else if (caar coefs + caar rcoeffs) neq p
  353. or numr addsq(cdar coefs,cdar rcoeffs) then return nil;
  354. coefs := cdr coefs;
  355. rcoeffs := cdr rcoeffs;
  356. j := j+1;
  357. go to a
  358. end;
  359. symbolic procedure solveabs u;
  360. begin scalar mu,var,lincoeff;
  361. var := cadr u;
  362. mu := caddr u;
  363. lincoeff := cadddr u;
  364. u := simp!* caar u;
  365. return append(solvesq(addsq(u,lincoeff),var,mu),
  366. solvesq(subtrsq(u,lincoeff),var,mu))
  367. end;
  368. put('abs,'solvefn,'solveabs);
  369. symbolic procedure solveexpt u;
  370. begin scalar c,mu,var,lincoeff;
  371. var := cadr u;
  372. mu := caddr u;
  373. lincoeff := cadddr u;
  374. u := car u;
  375. return if freeof(car u,var) % c**(...) = b.
  376. then <<if !*allbranch
  377. then <<!!arbint:=!!arbint+1;
  378. c:=list('times,2,'i,'pi,
  379. list('arbint,!!arbint))>>
  380. else c:=0;
  381. solvesq(subtrsq(simp!* cadr u,
  382. quotsq(addsq(simp!* list('log,mk!*sq lincoeff),
  383. simp!* c),
  384. simp!* list('log,car u))),var,mu)>>
  385. else if freeof(cadr u,var) % (...)**(m/n) = b;
  386. then if ratnump cadr u
  387. then solve!-fractional!-power(u,lincoeff,var,mu)
  388. else << % (...)**c = b.
  389. if !*allbranch
  390. then <<!!arbint:=!!arbint+1;
  391. c := mkexp list('quotient,
  392. list('times,2,'pi,
  393. list('arbint,!!arbint)),
  394. cadr u)>>
  395. % c := mkexp list('times,
  396. % list('arbreal,!!arbint))>>
  397. else c:=1;
  398. solvesq(subtrsq(simp!* car u,
  399. multsq(simp!* list('expt,
  400. mk!*sq lincoeff,
  401. mk!*sq invsq
  402. simp!* cadr u),
  403. simp!* c)),var,mu)>>
  404. % (...)**(...) = b : transcendental.
  405. else list list(list subtrsq(simp!*('expt . u),lincoeff),nil,mu)
  406. end;
  407. put('expt,'solvefn,'solveexpt);
  408. symbolic procedure solvelog u;
  409. solvesq(subtrsq(simp!* caar u,simp!* list('expt,'e,mk!*sq cadddr u)),
  410. cadr u,caddr u);
  411. put('log,'solvefn,'solvelog);
  412. symbolic procedure solvecos u;
  413. begin scalar c,d,z;
  414. if !*allbranch
  415. then <<!!arbint:=!!arbint+1;
  416. c:=list('times,2,'pi,list('arbint,!!arbint))>>
  417. else c:=0;
  418. c:=subtrsq(simp!* caar u,simp!* c);
  419. d:=simp!* list('acos,mk!*sq cadddr u);
  420. z := solvesq(subtrsq(c,d), cadr u,caddr u);
  421. if !*allbranch
  422. then z := append(solvesq(addsq(c,d), cadr u,caddr u),z);
  423. return z
  424. end;
  425. put('cos,'solvefn,'solvecos);
  426. symbolic procedure solvesin u;
  427. begin scalar c,d,f,z;
  428. if !*allbranch
  429. then <<!!arbint:=!!arbint+1;
  430. f:=list('times,2,'pi,list('arbint,!!arbint))>>
  431. else f:=0;
  432. c:=simp!* caar u;
  433. d:=list('asin,mk!*sq cadddr u);
  434. z := solvesq(subtrsq(c,simp!* list('plus,d,f)),cadr u,caddr u);
  435. if !*allbranch
  436. then z := append(solvesq(subtrsq(c,simp!* list('plus,'pi,
  437. mk!*sq subtrsq(simp!* f,simp!* d))),
  438. cadr u,caddr u),z);
  439. return z
  440. end;
  441. put('sin,'solvefn,'solvesin);
  442. symbolic procedure mkexp u;
  443. list('plus,list('cos,x),list('times,'i,list('sin,x)))
  444. where x = reval u;
  445. symbolic procedure solvecoeff(ex,var);
  446. % Ex is a standard form and var a kernel. Returns a list of
  447. % dotted pairs of exponents and coefficients (as standard quotients)
  448. % of var in ex, lowest power first, with exponents divided by their
  449. % gcd. This gcd is stored in !!GCD.
  450. begin scalar clist,oldkord;
  451. oldkord := updkorder var;
  452. clist := reorder ex;
  453. setkorder oldkord;
  454. clist := coeflis clist;
  455. !!gcd := caar clist;
  456. for each x in cdr clist do !!gcd := gcdn(car x,!!gcd);
  457. for each x in clist
  458. do <<rplaca(x,car x/!!gcd); rplacd(x,cdr x ./ 1)>>;
  459. return clist
  460. end;
  461. symbolic procedure solveroots(ex,var,mu);
  462. % Ex is a square and content free univariate standard form, var the
  463. % relevant variable and mu the root multiplicity. Finds insoluble,
  464. % complex roots of EX, returning a list of solve solutions.
  465. begin scalar y;
  466. y := reval list('roots,mkquote mk!*sq(ex ./ 1));
  467. if not(car y eq 'list)
  468. then errach list("incorrect root format",ex);
  469. return for each z in cdr y collect
  470. if not(car z eq 'equal) or cadr z neq var
  471. then errach list("incorrect root format",ex)
  472. else list(list simp caddr z,list var,mu)
  473. end;
  474. % ***** Procedures for solving a system of eqns *****
  475. symbolic procedure solvesys(exlist,varlis);
  476. % Exlist is a list of standard forms, varlis a list of kernels. If
  477. % the elements of varlis are linear in the elements of exlist, and
  478. % further the system of linear eqns so defined is non-singular, then
  479. % SOLVESYS returns a list of a list of standard quotients which are
  480. % solutions of the system, ordered as in varlis.
  481. begin scalar eqtype,oldkord;
  482. oldkord := setkorder varlis;
  483. exlist := for each j in exlist collect reorder j;
  484. % See if equations are linear or non-linear.
  485. if errorp errorset!*(list('solvenonlnrchk,mkquote exlist,
  486. mkquote varlis),nil)
  487. then eqtype := 'solvenonlnrsys
  488. else eqtype := 'solvelnrsys;
  489. % Solve for appropriate equation type.
  490. if eqtype eq 'solvenonlnrsys and null !*nonlnr
  491. then <<setkorder oldkord;
  492. rerror(solve,4,
  493. "Non linear equation solving not yet implemented")>>;
  494. exlist:=errorset!*(list(eqtype,mkquote exlist,mkquote varlis),t);
  495. setkorder oldkord;
  496. if errorp exlist then error1()
  497. else return if eqtype eq 'solvelnrsys then t . car exlist
  498. else car exlist
  499. end;
  500. symbolic procedure solvenonlnrchk(exlist,varlis);
  501. % Returns error if equations are nonlinear. (Error used to prevent
  502. % unnecessary computation.)
  503. for each ex in exlist do
  504. for each var in varlis do solvenonlnrchk1(ex,var,varlis);
  505. symbolic procedure solvenonlnrchk1(ex,var,varlis);
  506. if domainp ex then nil
  507. else if mvar ex=var
  508. then (if ldeg ex>1 or not freeofl(lc ex,varlis) then error1())
  509. else if not freeofl(mvar ex,varlis) and not(mvar ex member varlis)
  510. then error1()
  511. else <<solvenonlnrchk1(lc ex,var,varlis);
  512. solvenonlnrchk1(red ex,var,varlis)>>;
  513. endmodule;
  514. module ppsoln; % Solve surd eqns, mainly by principal of powers method.
  515. % Authors: Anthony C. Hearn and Stanley L. Kameny.
  516. fluid '(!*complex !*msg !*numval !*ppsoln);
  517. global '(bfone!*);
  518. !*ppsoln := t; % Keep this as internal switch.
  519. symbolic procedure solve!-fractional!-power(u,x,var,mu);
  520. % Attempts solution of equation car u**cadr u=x with respect to
  521. % kernel var and with multiplicity mu, where cadr u is a rational
  522. % number.
  523. begin scalar v,w,z;
  524. v := simp!* car u;
  525. w := simp!* cadr u;
  526. z := solvesq(subs2 subtrsq(exptsq(v,numr w),exptsq(x,denr w)),
  527. var,mu);
  528. w := subtrsq(simp('expt . u),x);
  529. z := check!-solns(z,numr w,var);
  530. return if z eq 'unsolved then list list(list w,nil,mu) else z
  531. end;
  532. symbolic procedure principal!-of!-powers!-soln(ex,x1,var,mu);
  533. % Finds solutions of ex=0 by the principal of powers method. Return
  534. % 'unsolved if solutions can't be found.
  535. begin scalar z;
  536. if null !*ppsoln then return 'unsolved;
  537. a: if null x1 then return 'unsolved
  538. else if suitable!-expt car x1
  539. and not((z := pr!-pow!-soln1(ex,car x1,var,mu)) eq 'unsolved)
  540. then return z;
  541. x1 := cdr x1;
  542. go to a
  543. end;
  544. symbolic procedure pr!-pow!-soln1(ex,y,var,mu);
  545. begin scalar oldkord,z;
  546. oldkord := updkorder y;
  547. z := reorder ex;
  548. setkorder oldkord;
  549. if ldeg z neq 1 then return 'unsolved;
  550. z := coeflis z;
  551. if length z neq 2 or caar z neq 0
  552. then errach list("solve confused",ex,z);
  553. z := exptsq(quotsq(negsq(cdar z ./ 1),cdadr z ./ 1),
  554. caddr caddr y);
  555. z := solvesq(subs2 addsq(simp!* cadr y,negsq z),var,mu);
  556. z := check!-solns(z,ex,var);
  557. return z
  558. end;
  559. symbolic procedure check!-solns(z,ex,var);
  560. begin scalar x,y,fv,sx,vs;
  561. fv := delete('i,freevarl(ex,var));
  562. % this does only one random setting!!
  563. if fv then for each v in fv do
  564. vs := (v . list('quotient,1+random 999,1000)) . vs;
  565. sx := if vs then numr subf(ex,vs) else ex;
  566. while z do
  567. if null cadar z then <<z := nil; x := 'unsolved>>
  568. else if
  569. <<y := numr subf(ex,list(caadar z . mk!*sq caaar z));
  570. null y
  571. % to do multiple random tests, the vs, sx setting and testing
  572. % would be moved here and done in a loop.
  573. or fv and null(y := numr subf(sx,list(caadar z .
  574. mk!*sq subsq(caaar z,vs))))
  575. or null numvalue y>>
  576. then <<x := car z . x; z := cdr z>>
  577. else z := cdr z;
  578. return if null x then 'unsolved else x
  579. end;
  580. symbolic procedure suitable!-expt u;
  581. eqcar(u,'expt) and eqcar(caddr u,'quotient) and cadr caddr u = 1
  582. and fixp caddr caddr u;
  583. symbolic procedure freevarl(ex,var);
  584. <<for each k in allkern list(ex ./ 1) do l := union(l,varsift(k,var));
  585. delete(var,l)>>
  586. where l=if var then list var else nil;
  587. symbolic procedure varsift(a,var);
  588. if atom a then
  589. if not(null a or numberp a or a eq var) then list a else nil
  590. else for each c in cdr a join varsift(c,var);
  591. symbolic procedure numvalue u;
  592. % Find floating point value of sf u.
  593. begin scalar !*numval,x,c,cp,p,m;
  594. m := !*msg; !*msg := nil;
  595. !*numval := t;
  596. c := ('i memq freevarl(u,nil));
  597. if (cp := !*complex) then off complex;
  598. x := setdmode('rounded,t); p := precision 10;
  599. if c then on complex;
  600. !*msg := m;
  601. u := numr simp prepf u;
  602. !*msg := nil;
  603. if c then off complex;
  604. if x then setdmode(x,t) else setdmode('rounded,nil);
  605. if cp then on complex; precision p;
  606. !*msg := m;
  607. return
  608. if eqcar(u,'!:rd!:) and (numvchk(100,z) where z=round!* u)
  609. or eqcar(u,'!:cr!:) and (numvchk(10,z) where z=retag crrl u)
  610. and (numvchk(10,z) where z=retag crim u)
  611. then nil else u
  612. end;
  613. symbolic procedure numvchk(fact,z);
  614. if atom z then fact*abs z<1
  615. else lessp!:(timbf(bfloat fact,abs!: z),bfone!*);
  616. endmodule;
  617. module glsolve; % Routines for solving a general system of linear eqns.
  618. % Author: Eberhard Schruefer.
  619. %**********************************************************************
  620. %*** The number of equations and the number of unknowns are ***
  621. %*** arbitrary i.e. the system can be under- or overdetermined. ***
  622. %*** Method used is Cramer's rule, realized through exterior ***
  623. %*** multiplication. ***
  624. %**********************************************************************
  625. fluid '(!*solvesingular kord!*);
  626. global '(!!arbint);
  627. % algebraic operator arbcomplex; % Already defined in main solve module.
  628. symbolic procedure solvelnrsys(u,v);
  629. % This is hook to general solve package. u is a list of polynomials
  630. % (s.f.'s) linear in the kernels of list v. Result is a tagged
  631. % standard form for the solutions.
  632. list list(glnrsolve(u,v),v,1);
  633. symbolic procedure glnrsolve(u,v);
  634. % U is a list of polynomials (s.f.'s) linear in the kernels of list
  635. % v. Result is an untagged list of solutions.
  636. begin scalar arbvars,sgn,x,y;
  637. while u and null(x := !*sf2ex(car u,v)) do u :=cdr u;
  638. for each j in u do if y := extmult(!*sf2ex(j,v),x) then x := y;
  639. if null x % all equations were zero.
  640. then return for each j in v collect !*f2q makearbcomplex();
  641. if inconsistency!-chk x
  642. then rerror(solve,5,"SOLVE given inconsistent equations");
  643. arbvars := for each j in setdiff(v,lpow x) collect
  644. j . makearbcomplex();
  645. if arbvars and null !*solvesingular
  646. then rerror(solve,6,"SOLVE given singular equations");
  647. if null red x then return
  648. for each j in v collect
  649. if y := atsoc(j,arbvars) then !*f2q cdr y else nil ./ 1;
  650. sgn := evenp length lpow x;
  651. return for each j in v collect if y := atsoc(j,arbvars)
  652. then !*f2q cdr y
  653. else mkglsol(j,x,sgn := not sgn,arbvars)
  654. end;
  655. symbolic procedure inconsistency!-chk u;
  656. null u or ((nil memq lpow u) and inconsistency!-chk red u);
  657. symbolic procedure mkglsol(u,v,sgn,arbvars);
  658. % u is the kernel to be solved for, x the exterior product of all
  659. % independent equations, sgn is the current sgn indicator, arbvars
  660. % is an a-list (var . arbvar).
  661. begin scalar s,x,y;
  662. x := nil ./ 1;
  663. y := lpow v;
  664. for each j on red v do
  665. if s := glsolterm(u,y,j,arbvars)
  666. then x := addsq(cancel(s ./ lc v),x);
  667. return if sgn then negsq x else x
  668. end;
  669. symbolic procedure glsolterm(u,v,w,arbvars);
  670. begin scalar x,y,sgn;
  671. x := lpow w;
  672. a: if null x then return
  673. if null car y then lc w
  674. else multf(cdr atsoc(car y,arbvars),
  675. if sgn then negf lc w else lc w);
  676. if car x eq u then return nil
  677. else if car x memq v then <<x := cdr x;
  678. if y then sgn := not sgn>>
  679. else if y then return nil
  680. else <<y := list car x; x := cdr x>>;
  681. go to a
  682. end;
  683. %**** Support for exterior multiplication ****
  684. % Data structure is lpow ::= list of variables in exterior product
  685. % lc ::= standard form
  686. symbolic procedure !*sf2ex(u,v);
  687. %Converts standardform u into a form distributed w.r.t. v
  688. %*** Should we check here if lc is free of v?
  689. if null u then nil
  690. else if domainp u or null(mvar u memq v) then list nil .* u .+ nil
  691. else list mvar u .* lc u .+ !*sf2ex(red u,v);
  692. symbolic procedure extmult(u,v);
  693. % Special exterior multiplication routine. Degree of form v is
  694. % arbitrary, u is a one-form.
  695. if null u or null v then nil
  696. else (if x then cdr x .* (if car x then negf subs2multf(lc u,lc v)
  697. else subs2multf(lc u,lc v))
  698. .+ extadd(extmult(!*t2f lt u,red v),
  699. extmult(red u,v))
  700. else extadd(extmult(red u,v),extmult(!*t2f lt u,red v)))
  701. where x = ordexn(car lpow u,lpow v);
  702. symbolic procedure subs2multf(u,v);
  703. (if denr x neq 1 then rerror(solve,7,"Sub error in glnrsolve")
  704. else numr x)
  705. where x = subs2(multf(u,v) ./ 1);
  706. symbolic procedure extadd(u,v);
  707. if null u then v
  708. else if null v then u
  709. else if lpow u = lpow v then
  710. (lambda x,y; if null x then y else lpow u .* x .+ y)
  711. (addf(lc u,lc v),extadd(red u,red v))
  712. else if ordexp(lpow u,lpow v) then lt u .+ extadd(red u,v)
  713. else lt v .+ extadd(u,red v);
  714. symbolic procedure ordexp(u,v);
  715. if null u then t
  716. else if car u eq car v then ordexp(cdr u,cdr v)
  717. else if null car u then nil
  718. else if null car v then t
  719. else ordop(car u,car v);
  720. symbolic procedure ordexn(u,v);
  721. %u is a single variable, v a list. Returns nil if u is a member
  722. %of v or a dotted pair of a permutation indicator and the ordered
  723. %list of u merged into v.
  724. begin scalar s,x;
  725. a: if null v then return(s . reverse(u . x))
  726. else if u eq car v then return nil
  727. else if u and ordop(u,car v) then
  728. return(s . append(reverse(u . x),v))
  729. else <<x := car v . x;
  730. v := cdr v;
  731. s := not s>>;
  732. go to a
  733. end;
  734. endmodule;
  735. module solvealg; % Solution of equations and systems which can
  736. % be lifted to algebraic (polynomial) systems.
  737. % Author: Herbert Melenk.
  738. % Copyright (c) 1990 The RAND Corporation and Konrad-Zuse-Zentrum.
  739. % All rights reserved.
  740. fluid '( system!* % system to be solved
  741. uv!* % user supplied variables
  742. iv!* % internal variables
  743. fv!* % restricted variables
  744. kl!* % kernels to be investigated
  745. sub!* % global substitutions
  746. inv!* % global inverse substitutions
  747. depl!* % REDUCE dependency list
  748. !*solvealgp % true if using this module
  749. );
  750. fluid '(!*trnonlnr);
  751. % If set on, the modified system and the Groebner result
  752. % or the reason for the failure are printed.
  753. global '(loaded!-packages!*);
  754. switch trnonlnr;
  755. !*solvealgp := t;
  756. % Solvenonlnrsys receives a system of standard forms and
  757. % a list of variables from SOLVE. The system is lifted to
  758. % a polynomial system (if possible) in substituting the
  759. % non-atomic kernels by new variables and appending additonal
  760. % relations, e.g.
  761. % replace add
  762. % sin u,cos u -> su,cu su^2+cu^2-1
  763. % u^(1/3) -> v v^3 - u
  764. % ...
  765. % in a recursive style. If completely successful, the
  766. % system definitely can be treated by Groebner or any
  767. % other polynomial system solver.
  768. %
  769. % Return value is a pair
  770. % (tag . res)
  771. % where "res" is nil or a structure for !*solvelist2solveeqlist
  772. % and "tag" is one of the following:
  773. %
  774. % T a satisfactory solution was generated,
  775. %
  776. % FAILED the algorithm cannot be applied (res=nil)
  777. %
  778. % INCONSISTENT the algorithm could prove that the
  779. % the system has no solution (res=nil)
  780. %
  781. % NIL the complexity of the system could
  782. % be reduced, but some (or all) relations
  783. % remain still implicit.
  784. symbolic procedure solvenonlnrsys(system!*,uv!*);
  785. % Main driver. We need non-local exits here
  786. % because of possibly hidden non algebraic variable
  787. % dependencies.
  788. if null !*solvealgp then '(failed) else % against recursion.
  789. (begin scalar iv!*,kl!*,inv!*,fv!*,r,!*solvealgp;
  790. for each f in system!* do solvealgk0 f;
  791. if !*trnonlnr then print list("original kernels:",kl!*);
  792. if atom errorset('(solvealgk1),!*trnonlnr,nil)
  793. then return '(failed);
  794. system!*:='list.for each p in system!* collect prepf p;
  795. if !*trnonlnr then
  796. << prin2t "Entering Groebner for system";
  797. writepri(mkquote system!*,'only);
  798. writepri(mkquote('list.iv!*), 'only);
  799. >>;
  800. if not('groebner memq loaded!-packages!*)
  801. then load!-package 'groebner;
  802. r := list(system!*,'list.iv!*);
  803. r := groesolveeval r;
  804. if !*trnonlnr then
  805. << prin2t "leaving Groebner with intermediate result";
  806. writepri(mkquote r,'only);
  807. terpri(); terpri();
  808. >>;
  809. return if r='(list) then '(inconsistent) else
  810. solvealginv r;
  811. end) where depl!*=depl!* ;
  812. symbolic procedure solvealgk0(p);
  813. % Extract new top level kernels from form p.
  814. if domainp p then nil else
  815. <<if not member(mvar p,kl!*)
  816. and not member(mvar p,iv!*)
  817. then kl!*:=mvar p.kl!*;
  818. solvealgk0(lc p);
  819. solvealgk0(red p)>>;
  820. symbolic procedure solvealgk1();
  821. % Process all kernels in kl!*. Note that kl!* might
  822. % change during processing.
  823. begin scalar k,kl0,kl1;
  824. k := car kl!*;
  825. while k do
  826. <<kl0 := k.kl0;
  827. solvealgk2(k);
  828. kl1 := kl!*; k:= nil;
  829. while kl1 and null k do
  830. if not member(car kl1,kl0) then k:=car kl1
  831. else kl1:=cdr kl1;
  832. >>;
  833. end;
  834. symbolic procedure solvealgk2(k);
  835. % Process one kernel.
  836. (if member(k,uv!*) then solvealgvb0 k and (iv!*:= k.iv!*) else
  837. if atom k then t else
  838. if eq(car k,'expt) then solvealgexpt(k,x) else
  839. if null x then t else
  840. if memq(car k,'(sin cos tan cot)) then
  841. solvealgtrig(k,x) else
  842. solvealggen(k,x)
  843. ) where x=solvealgtest(k);
  844. symbolic procedure solvealgtest(k);
  845. % Test if the arguments of a composite kernel interact with
  846. % the variables known so far.
  847. if atom k then nil else solvealgtest0(k);
  848. symbolic procedure solvealgtest0(k);
  849. % Test if kernel k interacts with the known variables.
  850. solvealgtest1(k,iv!*) or solvealgtest1(k,uv!*);
  851. symbolic procedure solvealgtest1(k,kl);
  852. % list of those kernels in list kl, which occur somewhere
  853. % in the composite kernel k.
  854. if null kl then nil else
  855. if atom k then if member(k,kl) then list k else nil else
  856. union(if smemq(car kl,cdr k) then list car kl else nil,
  857. solvealgtest1(k,cdr kl));
  858. symbolic procedure solvealgvb k;
  859. % Restricted variables are those which might establish
  860. % non-algebraic relations like e.g. x + e**x. Test k
  861. % and add it to the list.
  862. fv!* := append(solvealgvb0 k,fv!*);
  863. symbolic procedure solvealgvb0 k;
  864. % test for restricted variables.
  865. begin scalar ak;
  866. ak := allkernels(k,nil);
  867. if intersection(ak,iv!*) or intersection(ak,fv!*) then
  868. error(99,list("transcendental variable dependency from",k));
  869. return ak;
  870. end;
  871. symbolic procedure allkernels(a,kl);
  872. % a is an alebraic expression. Extract all possible inner
  873. % kernels of a and collect them in kl.
  874. if numberp a then kl else
  875. if atom a then if not member(a,kl) then a.kl else kl else
  876. <<for each x in cdr a do kl := allkernels1(numr simp x,kl);
  877. kl>>;
  878. symbolic procedure allkernels1(f,kl);
  879. if domainp f then kl else
  880. <<if not member(mvar f,kl) then
  881. kl:=allkernels(mvar f,mvar f . kl);
  882. allkernels1(lc f, allkernels1(red f,kl)) >>;
  883. symbolic procedure solvealgexpt(k,x);
  884. % kernel k is an exponential form.
  885. if null x then solvealgid k else
  886. ( if eqcar(m,'quotient) and fixp caddr m then
  887. if cadr m=1 then solvealgrad(cadr k,caddr m,x)
  888. else solvealgradx(cadr k,cadr m,caddr m,x)
  889. else solvealgexptgen(k,x)
  890. ) where m = caddr k;
  891. symbolic procedure solvealgexptgen(k,x);
  892. % Kernel k is a general exponentiation u ** v.
  893. begin scalar bas,xp,nv;
  894. bas := cadr k; xp := caddr k;
  895. if solvealgtest1(xp,uv!*) or solvealgtest1(bas,uv!*)
  896. then return solvealggen(k,x);
  897. % remaining case: "constant" exponential expression to
  898. % replaced by an id for syntatical reasons
  899. nv := '(
  900. % old kernel
  901. ( (expt alpha n))
  902. % new variable
  903. ( beta)
  904. % substitution
  905. ( ((expt alpha n) . beta) )
  906. % inverse
  907. ( (beta (expt alpha n) !& ))
  908. % new equations
  909. nil
  910. );
  911. nv:=subst(bas,'alpha,nv);
  912. nv:=subst(gensym(),'beta,nv);
  913. nv:=subst(xp,'n,nv);
  914. return solvealgupd(nv,nil);
  915. end;
  916. symbolic procedure solvealgradx(x,m,n,y);
  917. error(99,"forms e**(x/2) not yet implemented");
  918. symbolic procedure solvealgrad(x,n,y);
  919. % k is a radical exponentiation expression x**1/n.
  920. begin scalar nv,m;
  921. nv:= '(
  922. % old kernel
  923. ( (expt alpha (quotient 1 !&n)))
  924. % new variable
  925. ( beta)
  926. % substitution
  927. ( ((expt alpha (quotient 1 !&n)) . beta) )
  928. % inverse
  929. ( (beta alpha (expt !& !&n)) )
  930. % new equation
  931. ( (difference (expt beta !&n) alpha) )
  932. );
  933. m := list('alpha.x,'beta.gensym(),'!&n.n);
  934. nv := subla(m,nv);
  935. return solvealgupd(nv,y);
  936. end;
  937. symbolic procedure solvealgtrig(k,x);
  938. % k is a trigonometric function call.
  939. begin scalar nv,m,s;
  940. if cdr x then
  941. error(99,"too many variables in trig. function");
  942. x := car x;
  943. solvealgvb k;
  944. nv := '(
  945. % old kernels
  946. ( (sin alpha)(cos alpha)(tan alpha)(cot alpha) )
  947. % new variables
  948. ( (sin beta) (cos beta) )
  949. % substitutions
  950. ( ((sin alpha) . (sin beta))
  951. ((cos alpha) . (cos beta))
  952. ((tan alpha) . (quotient (sin beta) (cos beta)))
  953. ((cot alpha) . (quotient (cos beta) (sin beta))) )
  954. % inverses
  955. ( ((sin beta) !&x (sol (list (equal (sin alpha) !&!&))
  956. (list !&x)))
  957. ((cos beta) !&x (sol (list (equal (cos alpha) !&!&))
  958. (list !&x))))
  959. % new equation
  960. ( (plus (expt (sin beta) 2)(expt (cos beta) 2) -1) )
  961. );
  962. % invert the inner expression.
  963. s := solvealginner(cadr k,x);
  964. m := list('alpha . cadr k,
  965. 'beta . gensym(),
  966. '!&x . x,
  967. '!&!& . s);
  968. nv:=sublis(m , nv);
  969. return solvealgupd(nv,nil);
  970. end;
  971. symbolic procedure solvealggen(k,x);
  972. % k is a general function call; processable if SOLVE
  973. % can invert the function.
  974. begin scalar nv,m,s;
  975. if cdr x then
  976. error(99,"too many variables in function expression");
  977. x := car x;
  978. solvealgvb k;
  979. nv := '(
  980. % old kernels
  981. ( alpha )
  982. % new variables
  983. ( beta )
  984. % substitutions
  985. ( ( alpha . beta) )
  986. % inverses
  987. (( beta !&x !&!&))
  988. % new equation
  989. nil);
  990. % invert the kernel expression.
  991. s := solvealginner(k,x);
  992. m := list('alpha . k,
  993. 'beta . gensym(),
  994. '!&x . x,
  995. '!&!& . s);
  996. nv:=sublis(m , nv);
  997. return solvealgupd(nv,nil);
  998. end;
  999. symbolic procedure solvealgid k;
  1000. % k is a "constant" kernel, however in a syntax unprocessable
  1001. % for Groebner (e.g. expt(a/2)); replace temporarily
  1002. begin scalar nv,m,s;
  1003. nv := '(
  1004. % old kernels
  1005. ( alpha )
  1006. % new variables
  1007. ( )
  1008. % substitutions
  1009. ( ( alpha . beta) )
  1010. % inverses
  1011. (( beta nil . alpha))
  1012. % new equation
  1013. nil);
  1014. % invert the kernel expression.
  1015. m := list('alpha . k, 'beta . gensym());
  1016. nv:=sublis(m , nv);
  1017. return solvealgupd(nv,nil);
  1018. end;
  1019. symbolic procedure solvealginner(s,x);
  1020. <<s := solveeval list(list ('equal,s,'!&), list('list,x));
  1021. s := reval cadr s;
  1022. if not eqcar(s,'equal) or not equal(cadr s,x) then
  1023. error (99,"inner expression cannot be inverted");
  1024. caddr s>>;
  1025. symbolic procedure solvealgupd(u,innervars);
  1026. % Update the system and the structures.
  1027. begin scalar ov,nv,sub,inv,neqs;
  1028. ov := car u; u := cdr u;
  1029. nv := car u; u := cdr u;
  1030. sub:= car u; u := cdr u;
  1031. inv:= car u; u := cdr u;
  1032. neqs:=car u; u := cdr u;
  1033. for each x in ov do kl!*:=delete(x,kl!*);
  1034. for each x in innervars do
  1035. for each y in nv do depend1(y,x,t);
  1036. sub!* := append(sub,sub!*);
  1037. iv!* := append(nv,iv!*);
  1038. inv!* := append(inv,inv!*);
  1039. system!* := append(
  1040. for each u in neqs collect
  1041. <<u:= numr simp u; solvealgk0 u; u>>,
  1042. for each u in system!* collect numr subf(u,sub) );
  1043. return t;
  1044. end;
  1045. symbolic procedure solvealginv u;
  1046. % Reestablish the original variables, produce inverse
  1047. % mapping and do complete value propagation.
  1048. begin scalar v,r,s,m,lh,rh,y,z,tag,sub,expli,noarb,arbs;
  1049. integer n;
  1050. sub := for each p in sub!* collect (cdr p.car p);
  1051. tag := t;
  1052. return
  1053. tag .
  1054. (for each sol in cdr u collect
  1055. <<v:= r:= s:= noarb :=arbs :=nil;
  1056. for each eqn in reverse cdr sol do
  1057. <<lh := cadr eqn; rh := subsq(simp!* caddr eqn,s);
  1058. expli:=member(lh,iv!*);
  1059. if not expli then noarb := t;
  1060. if expli and not noarb then
  1061. << % assign value to free variables;
  1062. for each x in uv!* do
  1063. if smemq(x,rh) and not member(x,fv!*)
  1064. and not member(x,arbs)then
  1065. <<z := mvar makearbcomplex();
  1066. y := z; v := x . v; r := simp y . r;
  1067. % rh := subsq(rh,list(x.y));
  1068. % s := (x . y) . s;
  1069. arbs:=x.arbs;
  1070. >>;
  1071. s := (lh . prepsq rh) . s;
  1072. >>;
  1073. if (m:=assoc(lh,inv!*))then
  1074. <<lh :=cadr m;
  1075. rh:=simp!* subst(prepsq rh,'!&,caddr m)>>;
  1076. % append to the final output.
  1077. if (member(lh,uv!*) or not expli)
  1078. % inhibit repeateat same values.
  1079. and not<< z:=subsq(rh,sub);
  1080. n:=length member(z,r);
  1081. n>0 and lh=nth(v,n)>>
  1082. then <<r:=z.r; v:=lh.v;>>;
  1083. >>;
  1084. % classification of result quality.
  1085. for each x in uv!* do
  1086. if tag and not member(x,v) then tag:=nil;
  1087. reverse r . reverse v . list 1
  1088. >>);
  1089. end;
  1090. endmodule;
  1091. module solvetab; % Simplification rules for SOLVE.
  1092. % Author: David R. Stoutemyer.
  1093. % Modifications by: Anthony C. Hearn and Donald R. Morrison.
  1094. put('asin, 'inverse, 'sin);
  1095. put('acos, 'inverse, 'cos);
  1096. algebraic;
  1097. Comment Rules for reducing the number of distinct kernels in an
  1098. equation;
  1099. operator sol;
  1100. % for all a,b,c,d,x such that ratnump c and ratnump d let
  1101. % sol(a**c-b**d, x) = a**(c*lcm(c,d)) - b**(d*lcm(c,d));
  1102. for all a,b,c,d,x such that not fixp c and ratnump c and
  1103. not fixp d and ratnump d let
  1104. sol(a**c-b**d, x) = a**(c*lcm(den c,den d))
  1105. - b**(d*lcm(den c,den d));
  1106. for all a,b,c,d,x such that a freeof x and c freeof x let
  1107. sol(a**b-c**d, x) = e**(b*log a - d*log c);
  1108. for all a,b,c,d,x such that a freeof x and c freeof x let
  1109. sol(a*log b + c*log d, x) = b**a*d**c - 1,
  1110. sol(a*log b - c*log d, x) = b**a - d**c;
  1111. for all a,b,c,d,f,x such that a freeof x and c freeof x let
  1112. sol(a*log b + c*log d + f, x) = sol(log(b**a*d**c) + f, x),
  1113. sol(a*log b + c*log d - f, x) = sol(log(b**a*d**c) - f, x),
  1114. sol(a*log b - c*log d + f, x) = sol(log(b**a/d**c) + f, x),
  1115. sol(a*log b - c*log d - f, x) = sol(log(b**a/d**c) - f, x);
  1116. for all a,b,d,f,x such that a freeof x let
  1117. sol(a*log b + log d + f, x) = sol(log(b**a*d) + f, x),
  1118. sol(a*log b + log d - f, x) = sol(log(b**a*d) - f, x),
  1119. sol(a*log b - log d + f, x) = sol(log(b**a/d) + f, x),
  1120. sol(a*log b - log d - f, x) = sol(log(b**a/d) - f, x),
  1121. sol(log d - a*log b + f, x) = sol(log(d/b**a) + f, x),
  1122. sol(log d - a*log b - f, x) = sol(log(d/b**a) - f, x);
  1123. for all a,b,c,d,x such that a freeof x and c freeof x let
  1124. sol(a*log b + c*log d, x) = b**a*d**c - 1,
  1125. sol(a*log b - c*log d, x) = b**a - d**c;
  1126. for all a,b,d,x such that a freeof x let
  1127. sol(a*log b + log d, x) = b**a*d - 1,
  1128. sol(a*log b - log d, x) = b**a - d,
  1129. sol(log d - a*log b, x) = d - b**a;
  1130. for all a,b,c,x let
  1131. sol(log a + log b + c, x) = sol(log(a*b) + c, x),
  1132. sol(log a - log b + c, x) = sol(log(a/b) + c, x),
  1133. sol(log a + log b - c, x) = sol(log(a*b) - c, x),
  1134. sol(log a - log b - c, x) = sol(log(a/b) - c, x);
  1135. for all a,c,x such that c freeof x let
  1136. sol(log a + c, x) = a - e**(-c),
  1137. sol(log a - c, x) = a - e**c;
  1138. for all a,b,x let
  1139. sol(log a + log b, x) = a*b - 1,
  1140. sol(log a - log b, x) = a - b,
  1141. sol(cos a - sin b, x) = sol(cos a - cos(pi/2-b), x),
  1142. sol(sin a + cos b, x) = sol(sin a - sin(b-pi/2), x),
  1143. sol(sin a - cos b, x) = sol(sin a - sin(pi/2-b), x),
  1144. sol(sin a + sin b, x) = sol(sin a - sin(-b), x),
  1145. sol(sin a - sin b, x) = if !*allbranch then sin((a-b)/2)*
  1146. cos((a+b)/2) else a-b,
  1147. sol(cos a + cos b, x) = cos((a+b)/2)*cos((a-b)/2),
  1148. sol(cos a - cos b, x) = if !*allbranch then sin((a+b)/2)*
  1149. sin((a-b)/2) else a-b,
  1150. sol(asin a - asin b, x) = a-b,
  1151. sol(asin a + asin b, x) = a+b,
  1152. sol(acos a - acos b, x) = a-b,
  1153. sol(acos a + acos b, x) = a-b;
  1154. endmodule;
  1155. module quartic; % Procedures for solving cubic, quadratic and quartic
  1156. % eqns.
  1157. % Author: Anthony C. Hearn.
  1158. fluid '(!*sub2);
  1159. symbolic procedure multfq(u,v);
  1160. % Multiplies standard form U by standard quotient V.
  1161. begin scalar x;
  1162. x := gcdf(u,denr v);
  1163. return multf(quotf(u,x),numr v) ./ quotf(denr v,x)
  1164. end;
  1165. symbolic procedure quotsqf(u,v);
  1166. % Forms quotient of standard quotient U and standard form V.
  1167. begin scalar x;
  1168. x := gcdf(numr u,v);
  1169. return quotf(numr u,x) ./ multf(quotf(v,x),denr u)
  1170. end;
  1171. symbolic procedure cubertq u;
  1172. % Rationalizing the value in this and the following function leads
  1173. % usually to neater results.
  1174. % rationalizesq
  1175. simpexpt list(mk!*sq subs2!* u,'(quotient 1 3));
  1176. % SIMPRAD(U,3);
  1177. symbolic procedure sqrtq u;
  1178. % rationalizesq
  1179. simpexpt list(mk!*sq subs2!* u,'(quotient 1 2));
  1180. % SIMPRAD(U,2);
  1181. symbolic procedure subs2!* u; <<!*sub2 := t; subs2 u>>;
  1182. symbolic procedure solvequadratic(a2,a1,a0);
  1183. % A2, a1 and a0 are standard quotients.
  1184. % Solves a2*x**2+a1*x+a0=0 for x.
  1185. % Returns a list of standard quotient solutions.
  1186. begin scalar d;
  1187. d := sqrtq subtrsq(quotsqf(exptsq(a1,2),4),multsq(a2,a0));
  1188. a1 := quotsqf(negsq a1,2);
  1189. return list(subs2!* quotsq(addsq(a1,d),a2),
  1190. subs2!* quotsq(subtrsq(a1,d),a2))
  1191. end;
  1192. symbolic procedure solvecubic(a3,a2,a1,a0);
  1193. % A3, a2, a1 and a0 are standard quotients.
  1194. % Solves a3*x**3+a2*x**2+a1*x+a0=0 for x.
  1195. % Returns a list of standard quotient solutions.
  1196. % See Abramowitz and Stegun, Sect. 3.8.2, for details.
  1197. begin scalar q,r,sm,sp,s1,s2,x;
  1198. a2 := quotsq(a2,a3);
  1199. a1 := quotsq(a1,a3);
  1200. a0 := quotsq(a0,a3);
  1201. q := subtrsq(quotsqf(a1,3),quotsqf(exptsq(a2,2),9));
  1202. r := subtrsq(quotsqf(subtrsq(multsq(a1,a2),multfq(3,a0)),6),
  1203. quotsqf(exptsq(a2,3),27));
  1204. x := sqrtq addsq(exptsq(q,3),exptsq(r,2));
  1205. s1 := cubertq addsq(r,x);
  1206. s2 := if numr s1 then negsq quotsq(q,s1)
  1207. else cubertq subtrsq(r,x);
  1208. % This optimization only works if s1 is non zero.
  1209. sp := addsq(s1,s2);
  1210. sm := quotsqf(multsq(simp '(times i (sqrt 3)),subtrsq(s1,s2)),2);
  1211. x := subtrsq(sp,quotsqf(a2,3));
  1212. sp := negsq addsq(quotsqf(sp,2),quotsqf(a2,3));
  1213. return list(subs2!* x,subs2!* addsq(sp,sm),
  1214. subs2!* subtrsq(sp,sm))
  1215. end;
  1216. symbolic procedure solvequartic(a4,a3,a2,a1,a0);
  1217. % Solve the quartic equation a4*x**4+a3*x**3+a2*x**2+a1*x+a0 = 0,
  1218. % where the ai are standard quotients, using technique described in
  1219. % Section 3.8.3 of Abramowitz and Stegun;
  1220. begin scalar x,y,z;
  1221. % Convert equation to monomial form.
  1222. a3 := quotsq(a3,a4);
  1223. a2 := quotsq(a2,a4);
  1224. a1 := quotsq(a1,a4);
  1225. a0 := quotsq(a0,a4);
  1226. % Build and solve the resultant cubic equation. We select an
  1227. % arbitrary member of its set of solutions. Ideally we should
  1228. % only generate one solution, which should be the simplest.
  1229. y := subtrsq(exptsq(a3,2),multfq(4,a2));
  1230. % note that only first cubic solution is used here. We could save
  1231. % computation by using this fact.
  1232. x := car solvecubic(!*f2q 1,
  1233. negsq a2,
  1234. subs2!* subtrsq(multsq(a1,a3),multfq(4,a0)),
  1235. subs2!* negsq addsq(exptsq(a1,2),
  1236. multsq(a0,y)));
  1237. % Now solve the two equivalent quadratic equations.
  1238. y := sqrtq addsq(quotsqf(y,4),x);
  1239. z := sqrtq subtrsq(quotsqf(exptsq(x,2),4),a0);
  1240. a3 := quotsqf(a3,2);
  1241. x := quotsqf(x,2);
  1242. return append(solvequadratic(!*f2q 1,subtrsq(a3,y),subtrsq(x,z)),
  1243. solvequadratic(!*f2q 1,addsq(a3,y),addsq(x,z)))
  1244. end;
  1245. endmodule;
  1246. end;